diff --git a/.gitignore b/.gitignore index a9c44f0..d7d39b0 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ tracker/tracker tracker/download tracker/keep tracker/ssdv_done_* -tracker/coonvert_* +tracker/convert_* tracker/tweets.log tracker/webstuff.py tracker/batc @@ -18,7 +18,8 @@ tracker/ex_* *.old telemetry.txt gps.txt -take* +prediction.txt +take_pic* convert* *.bin accelcal.txt diff --git a/README.md b/README.md index a53e22b..572b991 100755 --- a/README.md +++ b/README.md @@ -1,233 +1,365 @@ -# PITS - HAB Tracker software for the PITS boards # - -Created by Dave Akerman (dave@sccs.co.uk) - -This software is written for the PITS Zero with Pi Zero, PITS+ with the various A+/B+ models, and the original PITS with the Pi A/B boards. - -PITS Zero and PITS+ can be purchased from board from http://ava.upuaut.net/store/. - -Software support is provided for customers who have purchased a PITS, PITS Zero or PITS+ board, for use with that board only. - -## Installation ## - -Follow the instructions at http://www.pi-in-the-sky.com/index.php?id=sd-card-image-from-scratch - - -## USB Camera ## - -To use a USB camera (i.e. a compact or SLR) instead of the Pi camera, you need to install gphoto2 and imagemagick: - - sudo apt-get install gphoto2 imagemagick - -and you need to edit /boot/pisky.txt and change the "camera" line to be: - - Camera=G - -(G/g are for gphoto2, U/F/u/f for fswebcam; N/n is for no camera, Y/y/1/TC/c is for CSI camera). - -The camera must be able to support remote capture as well as image download, and this excludes a large number of cameras in particular all Canon compacts since 2009. -There's a list of cameras at http://www.gphoto.org/doc/remote/. - -The image resolution is not controlled so you should set this up in the camera. The full image files are stored on Pi SD card, so ensure that it has sufficient free capacity. - -If you are transmitting images with SSDV, then these need to be resized before transmission. This is configured in pisky.txt in the usual way. -The process though is different - the camera takes the full sized image, and this image is downloaded to the Pi unaltered. Before transmission the selected images are then resized using imagemagick. -So, regardless of the image size settings in pisky.txt, the Pi SD card will contain the full-size images. - -PITS does not set the camera exposure. Normally with the camera set to an automatic exposure mode you will get plenty of good images, but you can of course set it to any mode you wish including manual. -If you wish to control this from the software rather than on the camera, then create an executable script "take_image" (there is a sample supplied as "take_image.sample"). -Note that most settings are only available in the more basic modes - e.g. the aperture can only be controlled in aperture-priority or manual modes. - -gphoto2 needs more work than the other camera options, and we strongly recommend some experimentation with the camera you intend to use. See http://gphoto.org/doc/manual/using-gphoto2.html. - - -## USB Webcam ## - -To use a USB webcam instead of the Pi camera, you need to install fswebcam: - - sudo apt-get install fswebcam - -and you need to edit /boot/pisky.txt and change the "camera" line to be: - - Camera=U - - -## IMAGE PROCESSING ## - -All images now include telemetry (longitude, latitude, altitude) in the JPEG comment field, **but only if EXIV2 has been installed**. - -It is therefore possible to overlay downloaded images with telemetry data, as text or graphics, using (for example) ImageMagick, and EXIV2 to extract the data from the JPEG being processed. A sample script "process_image.sample" is provided; to use this, rename to be "process_image", make it executable, and edit to your requirements. Please note that the sample assumes a particular image resolution and you will need to change the pixel positions of the various items. Imagemagick is quite complex to use but there is plenty of documentation on the web plus many samples. - - -## Change Log ## - -04/02/2018 -========== - -- Fixed APRS pre-emphasis which was actually de-emphasis! - -21/09/2017 -========== - -- Add LoRa mode 8 for SSDV repeater network - -15/09/2017 -========== - -- Re-enabled temperature sensing for Pi Zero / W - -05/09/2017 -========== - -- Print list of fields when sending first sentence for a channel. Useful for setting up payload document. - -01/09/2017 -========== - -- Reduce amount of Rx-related guff when in listen-only mode - -19/08/2017 -========== - -- Changed startup to use systemd - -10/04/2017 -========== - -- Added support direct upload to Habitat using an internet connection - -03/03/2017 -========== - -- Added support for Pi Zero W - -07/11/2016 -========== - -- Added support for USB cameras via gphoto2 - -19/09/2016 -========== - -- Red (warn) LED now flashes during wait for GPS lock; if it's on without flashing then there is no GPS data (which means GPS misconfigured, or Pi revision not recognised) - -18/09/2016 -========== - -- Full list of Pi Zero boards -- Ignore empty CRLF lines in pisky.txt - -29/09/2016 -========== - -- Better CPU/Pi test - -25/09/2016 -========== - -- Added SSDV_settings to pisky.txt - add command-line parameter when calling SSDV program- Removed support for running without device tree -- Fixed setting of implicit/explicit mode manually, ditto low-data-rate optimisation -- Truncate SSDV images larger than max size (currently 4096 packets) - -12/09/2016 -========== - -- Settable delay between packets -- Minimal "buoy mode" telemetry - -14/08/2016 -========== - -- Added option for serial GPS connection. Use GPS_Device=/dev/ttyACM0 for example. -- Re-enabled APRS script - -12/08/2016 -========== - -- Fixed error where LoRa channel stopped transmitting after a few sentences if SSDV disabled on that channel -- Increased maximum size of SSDV file to 4096 packets -- Add option to disable reading of ADC, and also transmission of ADC values - -17/06/2016 -========== - -- BME280 driver - -18/05/2015 -========== - -- Ability to receive SMS-style messages uplinked from the ground via LoRa. Messages are saved to a text file and can then be processed externally (e.g. by supplied Python script to display on a Pi Sense HAT) -- Ability to include, in the LoRa telemetry, CSV data from an external file. Sample Python script supplied to build that file from Sense HAT sensor data. -- Ability to re-send SSDV packets, via LoRa, that were not received on the ground. Uses a LoRa uplink message to define which packets need to be sent. -- Can include uplink message status in LoRa telemetry. -- Photo sizes forced to be no smaller than 320x240, to avoid limitation in raspistill program. -- RTTY can be switched off (no data no carrier) during LoRa uplink periods. -- Use of EXIV2 (if installed) to insert telemetry data into all stored JPEG images, in the JPEG comment field -- Sample image processing script to extract telemetry from JPEG comment field, and insert into downloaded images as a text overlay, using ImageMagick - -01/03/2016 +# PITS - HAB Tracker software for the PITS boards # + +Created by Dave Akerman (dave@sccs.co.uk) + +This software is written for the PITS Zero with Pi Zero, PITS+ with the various A+/B+ models, and the original PITS with the Pi A/B boards. + +PITS Zero and PITS+ can be purchased from board from http://ava.upuaut.net/store/. + +Software support is provided for customers who have purchased a PITS, PITS Zero or PITS+ board, for use with that board only. + +## Installation ## + +Follow the instructions at http://www.pi-in-the-sky.com/index.php?id=sd-card-image-from-scratch + + +## Piezo Buzzer ## + +A piezo buzzer is another helpful device for locating a landed payload. Use one that will operate from 3V and consumes less than 20mA; I use RS 617-3069 which is very loud. Connect to a 0V pin on the Pi GPIO connector, and a spare I/O pin. + +Configure as follows, being careful to use a WiringPi pin number: + + Piezo_Pin=4 + Piezpo_Alt=1000 + +For the above settings, the buzzer light will come on during flight once the payload descends below 1000m. + + + +## Strobon LED strobe light ## + +These are **very** bright LED strobe lights made for RC models. You can power them from the Pi 5V and 0V lines; note that they do pull 1A for 20ms for each flash, which might cause issues depending on the power supply; if in doubt add some capacitance at the strobe. + +The LED uses a PWM input to control its mode. Choose any free pin, e.g. **BCM** pin 18 which is only used if you have an APRS board. Configure as follows, being careful to use BCM pin numbers and **not** WiringPi pin numbers: + + + Strobe_Pin=18 + Strobe_Alt=1000 + +For the above settings, the strobe light will come on during flight once the payload descends below 1000m. + +Both V1 and V2 Strobons have been tested to work fine. PITS controls the LED by using a standard 50Hz PWM signal set with 1ms pulse for off and 2ms for on. Note that when powered up, the V2 model strobes until PITS starts; the V1 starts up as off. + + +## Additional Sensors ## + +You can enable any **one** of these supported sensor devices using these lines in pisky.txt: + + Enable_BME280=Y + Enable_MS5611=Y + Enable_BMP085=Y + +All of them measure pressure and temperature; the BME280 also measures humidity. + + +## RTTY through LoRa ## + +To understand the settings, first take note that PITS has a concept of "radio channels" where a channel is a particular radio transmitter not mode (RTTY or LoRa).  We are using one of the LoRa devices (channels) to transmit RTTY.  So our settings are associated with the particular LoRa module (in CE0 or CE1 position).  Essentially we are overriding the normal LoRa functionality by telling the software to transmit RTTY as well as or instead of the LoRa packets. + +These are the new settings (shown for channel 0) + + +- LORA\_RTTY\_Frequency_0=.  Without this, RTTY will use the same frequency as LoRa.  I recommend that you keep the frequencies apart so that your RTTY receiving software does not try to track the LoRa trransmissions. +- LORA\_RTTY\_Baud_0=.  Choose 50 (better range) or 300 (faster, allows for SSDV, easier for dl-fldigi to lock to). +- LORA\_RTTY\_Shift_0=.  The carrier shift must be numerically greater than the baud rate.  Note that the LoRa chip steps in multiples of 30.5Hz. +- LORA\_RTTY\_Count_0=.  This is how many RTTY packets are sent one after the other before transmitting any LoRa packets.  2 is recommended in case the RTTY decoder misses the start of the first packet. +- LORA\_RTTY\_Every_0=.  This is how many LoRa packets are sent one after the other before transmitting any RTTY packets.  Set to zero to disable LoRa (and only send RTTY). +- LORA\_RTTY\_Preamble_0=.  Sets the length of preamble (constant carrier) before sending RTTY data.  Default is 8 and seems to be plenty. + +Example: + + LORA_RTTY_Frequency_0=434.350 + LORA_RTTY_Baud_0=300 + LORA_RTTY_Shift_0=610 + LORA_RTTY_Count_0=2 + LORA_RTTY_Every_0=12 + LORA_RTTY_Preamble_0=8 + + +## USB Camera ## + +To use a USB camera (i.e. a compact or SLR) instead of the Pi camera, you need to install gphoto2 and imagemagick: + + sudo apt-get install gphoto2 imagemagick + +and you need to edit /boot/pisky.txt and change the "camera" line to be: + + Camera=G + +(G/g are for gphoto2, U/F/u/f for fswebcam; N/n is for no camera, Y/y/1/TC/c is for CSI camera). + +The camera must be able to support remote capture as well as image download, and this excludes a large number of cameras in particular all Canon compacts since 2009. +There's a list of cameras at http://www.gphoto.org/doc/remote/. + +The image resolution is not controlled so you should set this up in the camera. The full image files are stored on Pi SD card, so ensure that it has sufficient free capacity. + +If you are transmitting images with SSDV, then these need to be resized before transmission. This is configured in pisky.txt in the usual way. +The process though is different - the camera takes the full sized image, and this image is downloaded to the Pi unaltered. Before transmission the selected images are then resized using imagemagick. +So, regardless of the image size settings in pisky.txt, the Pi SD card will contain the full-size images. + +PITS does not set the camera exposure. Normally with the camera set to an automatic exposure mode you will get plenty of good images, but you can of course set it to any mode you wish including manual. +If you wish to control this from the software rather than on the camera, then create an executable script "take_image" (there is a sample supplied as "take_image.sample"). +Note that most settings are only available in the more basic modes - e.g. the aperture can only be controlled in aperture-priority or manual modes. + +gphoto2 needs more work than the other camera options, and we strongly recommend some experimentation with the camera you intend to use. See http://gphoto.org/doc/manual/using-gphoto2.html. + + +## USB Webcam ## + +To use a USB webcam instead of the Pi camera, you need to install fswebcam: + + sudo apt-get install fswebcam + +and you need to edit /boot/pisky.txt and change the "camera" line to be: + + Camera=U + + +## IMAGE PROCESSING ## + +All images now include telemetry (longitude, latitude, altitude) in the JPEG comment field, **but only if EXIV2 has been installed**. + +It is therefore possible to overlay downloaded images with telemetry data, as text or graphics, using (for example) ImageMagick, and EXIV2 to extract the data from the JPEG being processed. A sample script "process_image.sample" is provided; to use this, rename to be "process_image", make it executable, and edit to your requirements. Please note that the sample assumes a particular image resolution and you will need to change the pixel positions of the various items. Imagemagick is quite complex to use but there is plenty of documentation on the web plus many samples. + + +## Change Log ## + +07_06_2024 ========== -- Test for latest Pi boards; assume PITS+ if not a known board -- clear.txt now actually works -- Better setting of MTX2 frequency - -28/11/2015 - -- Suport for Pi Zero -- Support for USB webcam via fswebcam - -12/10/2015 - -- Added "camera_settings" option for config file -- Fixed IP check which faulted when Pi on a VPN - -18/08/2015 - -- Merged in LoRa code (LoRa branch is now defunct) -- Photographs can now be taken at independent rates for RTTY, LoRa, full-size -- Landing prediction option -- Fixes for multiple DS18B20 sensors -- Ability to run emulated flight from GPS NMEA file -- Ability to modify SSDV images before transmission using ImageMagick etc. -- LoRa Calling Mode -- RTTY serial port kept open continuously -- Startup radio message with IP address etc. -- Stop multiple tracker programs from being run -- Test for latest Pi board -- Option to delete existing image files at startup -- Image files now in names/dated folders e.g. /home/pi/images/RTTY/18_08_2015 - -17/06/2015 - -- Merged in APRS code (APRS branch is now defunct) -- Added options to control when APRS packets are sent -- Fixed issue with tracker program failing SD card is pulled and APRS enabled -- NTX2 frequency-setting code now has same improvements as for MTX2 -- Serial port kept open now instead of open/close each packet; using flush command to sync instead of closing. This allows ... -- ... Telemetry log entries now written whilst waiting for telemetry to Tx; removes/reduces delay due to SD card writing. - -01/06/2015 - -- MTX2 frequency-setting code changed to fix random fails to set frequency -- logging now on by default -- log files removed from repository -- Long payload IDs are truncated to 6 before passing to SSDV program -- SSDV image widths and heights now forced to be multiples of 16 pixels -- Added support for second (external) DS18B20 temperature sensor -- Fix to occassional missing packets -- Support for Pi V2 B -- Protect against BMP085/180 being disconnected after initialisation - -19/12/2014 - -- GPS code completely re-written to use WiringPi library instead of bcm library -- default configuration now leaves the monitor on, to ease development -- As the PITS+ boards are set by frequency in MHz, but the earlier board was - set by channel number, the code now accepts either "frequency=nn" for channel - number, of frequency=xxx.xxxMHz for actual frequency. Not that using the - second form does not give you more options on the older board - the frequency - will be set to the closest channnel. -- Camera filenames are now the system time -- Camera images are now saved in dated folders - +- Added support for new camera commands replacing raspistill. Use "R" for camera type + +06_06_2021 +========== + +- Fixed bug that killed RTTY enable pin during initialisation if no cutdown pin configured + +20_04_2021 +========== + +- Fixed SSDV image number not wrapping round +- Fixed frequency error calculation on Rx +- Allow for uplink if GPS lost + +21_01_2021 +========== + +- Extend uplink options + +02/03/2020 +========== + +- Fix GPU temperature only being read once if there's no DS18B20 (i.e. not using PITS board) + +14/02/2020 +========== + +- Disable null packets for LoRa mode 0 + +27/08/2019 +========== + +- Pedestrian mode for UBlox +- Use Flight_Mode_Altitude setting to choose flight mode or pedestrian for UBlox and L80 +- Set height and/or width to zero to tell raspistill to use full camera resolution for "full" mode images + +08/07/2019 +========== + +- Removed some warnings from the latest gcc in Raspbian Buster + +22/11/2018 +========== + +- Added support for Strobon V1 and V2 PWM-controlled LED strobe lights + +21/11/2018 +========== + +- Added support for MS5611 Pressure/Temperature Sensor + +26/09/2018 +========== + +- Added support for RTTY via LoRa module + +09/04/2018 +========== + +- Added HABPack support for LoRa, using encoder by Phil Crump. Enable with LORA_HABPack_n=Y + +04/02/2018 +========== + +- Fixed APRS pre-emphasis which was actually de-emphasis! + +21/09/2017 +========== + +- Add LoRa mode 8 for SSDV repeater network + +15/09/2017 +========== + +- Re-enabled temperature sensing for Pi Zero / W + +05/09/2017 +========== + +- Print list of fields when sending first sentence for a channel. Useful for setting up payload document. + +01/09/2017 +========== + +- Reduce amount of Rx-related guff when in listen-only mode + +19/08/2017 +========== + +- Changed startup to use systemd + +10/04/2017 +========== + +- Added support direct upload to Habitat using an internet connection + +03/03/2017 +========== + +- Added support for Pi Zero W + +07/11/2016 +========== + +- Added support for USB cameras via gphoto2 + +19/09/2016 +========== + +- Red (warn) LED now flashes during wait for GPS lock; if it's on without flashing then there is no GPS data (which means GPS misconfigured, or Pi revision not recognised) + +18/09/2016 +========== + +- Full list of Pi Zero boards +- Ignore empty CRLF lines in pisky.txt + +29/09/2016 +========== + +- Better CPU/Pi test + +25/09/2016 +========== + +- Added SSDV_settings to pisky.txt - add command-line parameter when calling SSDV program- Removed support for running without device tree +- Fixed setting of implicit/explicit mode manually, ditto low-data-rate optimisation +- Truncate SSDV images larger than max size (currently 4096 packets) + +12/09/2016 +========== + +- Settable delay between packets +- Minimal "buoy mode" telemetry + +14/08/2016 +========== + +- Added option for serial GPS connection. Use GPS_Device=/dev/ttyACM0 for example. +- Re-enabled APRS script + +12/08/2016 +========== + +- Fixed error where LoRa channel stopped transmitting after a few sentences if SSDV disabled on that channel +- Increased maximum size of SSDV file to 4096 packets +- Add option to disable reading of ADC, and also transmission of ADC values + +17/06/2016 +========== + +- BME280 driver + +18/05/2015 +========== + +- Ability to receive SMS-style messages uplinked from the ground via LoRa. Messages are saved to a text file and can then be processed externally (e.g. by supplied Python script to display on a Pi Sense HAT) +- Ability to include, in the LoRa telemetry, CSV data from an external file. Sample Python script supplied to build that file from Sense HAT sensor data. +- Ability to re-send SSDV packets, via LoRa, that were not received on the ground. Uses a LoRa uplink message to define which packets need to be sent. +- Can include uplink message status in LoRa telemetry. +- Photo sizes forced to be no smaller than 320x240, to avoid limitation in raspistill program. +- RTTY can be switched off (no data no carrier) during LoRa uplink periods. +- Use of EXIV2 (if installed) to insert telemetry data into all stored JPEG images, in the JPEG comment field +- Sample image processing script to extract telemetry from JPEG comment field, and insert into downloaded images as a text overlay, using ImageMagick + +01/03/2016 +========== + +- Test for latest Pi boards; assume PITS+ if not a known board +- clear.txt now actually works +- Better setting of MTX2 frequency + +28/11/2015 + +- Suport for Pi Zero +- Support for USB webcam via fswebcam + +12/10/2015 + +- Added "camera_settings" option for config file +- Fixed IP check which faulted when Pi on a VPN + +18/08/2015 + +- Merged in LoRa code (LoRa branch is now defunct) +- Photographs can now be taken at independent rates for RTTY, LoRa, full-size +- Landing prediction option +- Fixes for multiple DS18B20 sensors +- Ability to run emulated flight from GPS NMEA file +- Ability to modify SSDV images before transmission using ImageMagick etc. +- [LoRa Calling Mode](http://www.daveakerman.com/?p=1816) +- RTTY serial port kept open continuously +- Startup radio message with IP address etc. +- Stop multiple tracker programs from being run +- Test for latest Pi board +- Option to delete existing image files at startup +- Image files now in names/dated folders e.g. /home/pi/images/RTTY/18_08_2015 + +17/06/2015 + +- Merged in APRS code (APRS branch is now defunct) +- Added options to control when APRS packets are sent +- Fixed issue with tracker program failing SD card is pulled and APRS enabled +- NTX2 frequency-setting code now has same improvements as for MTX2 +- Serial port kept open now instead of open/close each packet; using flush command to sync instead of closing. This allows ... +- ... Telemetry log entries now written whilst waiting for telemetry to Tx; removes/reduces delay due to SD card writing. + +01/06/2015 + +- MTX2 frequency-setting code changed to fix random fails to set frequency +- logging now on by default +- log files removed from repository +- Long payload IDs are truncated to 6 before passing to SSDV program +- SSDV image widths and heights now forced to be multiples of 16 pixels +- Added support for second (external) DS18B20 temperature sensor +- Fix to occassional missing packets +- Support for Pi V2 B +- Protect against +- +- +- +- +- 085/180 being disconnected after initialisation + +19/12/2014 + +- GPS code completely re-written to use WiringPi library instead of bcm library +- default configuration now leaves the monitor on, to ease development +- As the PITS+ boards are set by frequency in MHz, but the earlier board was + set by channel number, the code now accepts either "frequency=nn" for channel + number, of frequency=xxx.xxxMHz for actual frequency. Not that using the + second form does not give you more options on the older board - the frequency + will be set to the closest channnel. +- Camera filenames are now the system time +- Camera images are now saved in dated folders + diff --git a/boot/pisky.txt b/boot/pisky.txt index ef7b394..596b847 100644 --- a/boot/pisky.txt +++ b/boot/pisky.txt @@ -2,7 +2,7 @@ payload=CHANGEME disable_monitor=N frequency=434.250 baud=300 -camera=Y +camera=R low_width=320 low_height=240 high=2000 @@ -15,11 +15,13 @@ adc_vmax=18.5 logging=GPS,Telemetry Disable_RTTY=N info_messages=2 +Send_Field_List=N full_low_width=640 full_low_height=480 -full_high_width=2592 -full_high_height=1944 +# 0 in either of the following tells the software to use full resolution from the Pi camera +full_high_width=0 +full_high_height=0 full_image_period=60 #APRS_Callsign=CHANGE @@ -30,8 +32,11 @@ APRS_Random=5 #LORA_Frequency_0=434.225 #LORA_Payload_0=CHANGEME #LORA_Mode_0=0 +#LORA_Calling_Frequency_0=433.650 +#LORA_Calling_Count_0=5 #LORA_Frequency_1=434.275 #LORA_Payload_1=CHANGEME #LORA_Mode_1=1 - +#LORA_Calling_Frequency_1=433.650 +#LORA_Calling_Count_1=5 diff --git a/tracker/DS18B20.c b/tracker/DS18B20.c index b8d8f04..53508f8 100755 --- a/tracker/DS18B20.c +++ b/tracker/DS18B20.c @@ -26,6 +26,8 @@ void *DS18B20Loop(void *some_void_ptr) int SensorCount; GPS = (struct TGPS *)some_void_ptr; + + GPS->DS18B20Count = 0; while (1) { @@ -38,7 +40,7 @@ void *DS18B20Loop(void *some_void_ptr) { if ((dp->d_name[0] != 'W') && (dp->d_name[2] == '-')) { - sprintf(filename, "%s/%s/w1_slave", folder, dp->d_name); + sprintf(filename, "%.40s/%.30s/w1_slave", folder, dp->d_name); if ((fp = fopen(filename, "r")) != NULL) { // 44 02 4b 46 7f ff 0c 10 ee : crc=ee YES @@ -85,7 +87,6 @@ void *DS18B20Loop(void *some_void_ptr) fscanf (fp, "%lf", &T); GPS->DS18B20Temperature[0] = T / 1000; // printf ("GPU temperature is %6.3f C.\n", GPS->DS18B20Temperature[0]); - GPS->DS18B20Count = 1; fclose (fp); } } diff --git a/tracker/MS5611.c b/tracker/MS5611.c new file mode 100755 index 0000000..0a88035 --- /dev/null +++ b/tracker/MS5611.c @@ -0,0 +1,229 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gps.h" +#include "misc.h" +#include "MS5611.h" + +#define MS5611_ADDRESS 0x77 + +#define MS5611_CMD_ADC_READ 0x00 +#define MS5611_CMD_RESET 0x1E +#define MS5611_CMD_CONV_D1 0x40 +#define MS5611_CMD_CONV_D2 0x50 +#define MS5611_CMD_READ_PROM 0xA2 + +#define MS5611_ULTRA_HIGH_RES 0x08 +#define MS5611_HIGH_RES 0x06 +#define MS5611_STANDARD 0x04 +#define MS5611_LOW_POWER 0x02 +#define MS5611_ULTRA_LOW_POWER 0x00 + +void ms5611WriteRegister(int fd, int Value) +{ + unsigned char buf[1]; + + buf[0] = Value; + + if ((write(fd, buf, 1)) != 1) + { + printf("Error writing to MS5611\n"); + } +} + +uint16_t ms5611ReadRegister16(int fd, uint8_t address) +{ + unsigned char buf[2]; + + buf[0] = address; + + if ((write(fd, buf, 1)) != 1) { // Send register we want to read from + printf("Error writing to i2c slave\n"); + return -1; + } + + if (read(fd, buf, 2) != 2) { // Read back data into buf[] + printf("Unable to read from slave\n"); + return -1; + } + + return (uint16_t) buf[0]<<8 | buf[1]; +} + +int32_t ms5611ReadRegister24(int fd, uint8_t address) +{ + unsigned char buf[3]; + + buf[0] = address; + + // Send register we want to read from + if ((write(fd, buf, 1)) != 1) + { + printf("Error writing to i2c slave\n"); + return -1; + } + + // Read back data into buf[] + if (read(fd, buf, 3) != 3) + { + printf("Unable to read from slave\n"); + return -1; + } + + return (int32_t) buf[0]<<16 | buf[1]<<8 | buf[2]; +} + +void ms5611ReadPROM(int fd, uint16_t *fc) +{ + uint8_t offset; + + for (offset = 0; offset < 6; offset++) + { + fc[offset] = ms5611ReadRegister16(fd, MS5611_CMD_READ_PROM + (offset * 2)); + } +} + +uint32_t ms5611ReadRawTemperature(int fd) +{ + ms5611WriteRegister(fd, MS5611_CMD_CONV_D2 + MS5611_ULTRA_HIGH_RES); + + delay(10); + + return ms5611ReadRegister24(fd, MS5611_CMD_ADC_READ); +} + +uint32_t ms5611ReadRawPressure(int fd) +{ + ms5611WriteRegister(fd, MS5611_CMD_CONV_D1 + MS5611_ULTRA_HIGH_RES); + + delay(10); + + return ms5611ReadRegister24(fd, MS5611_CMD_ADC_READ); +} + +double ms5611ReadTemperature(int fd, uint16_t *fc, int compensation) +{ + uint32_t D2; + int32_t dT, TEMP, TEMP2; + + D2 = ms5611ReadRawTemperature(fd); + dT = D2 - (uint32_t)fc[4] * 256; + TEMP = 2000 + ((int64_t) dT * fc[5]) / 8388608; + TEMP2 = 0; + + if (compensation) + { + if (TEMP < 2000) + { + TEMP2 = (dT * dT) / (2 << 30); + } + } + + TEMP = TEMP - TEMP2; + + return ((double)TEMP/100); +} + + +double ms5611ReadPressure(int fd, uint16_t *fc, int compensation) +{ + uint32_t D1, D2; + int32_t dT; + int64_t OFF, SENS, TEMP3; + + D1 = ms5611ReadRawPressure(fd); + + D2 = ms5611ReadRawTemperature(fd); + + dT = D2 - (uint32_t)fc[4] * 256; + + OFF = (int64_t)fc[1] * 65536 + (int64_t)fc[3] * dT / 128; + + SENS = (int64_t)fc[0] * 32768 + (int64_t)fc[2] * dT / 256; + + if (compensation) + { + int32_t TEMP; + int64_t OFF2, SENS2; + + TEMP = 2000 + ((int64_t) dT * fc[5]) / 8388608; + + OFF2 = 0; + SENS2 = 0; + + if (TEMP < 2000) + { + OFF2 = 5 * ((TEMP - 2000) * (TEMP - 2000)) / 2; + SENS2 = 5 * ((TEMP - 2000) * (TEMP - 2000)) / 4; + } + + if (TEMP < -1500) + { + OFF2 = OFF2 + 7 * ((TEMP + 1500) * (TEMP + 1500)); + SENS2 = SENS2 + 11 * ((TEMP + 1500) * (TEMP + 1500)) / 2; + } + + OFF = OFF - OFF2; + SENS = SENS - SENS2; + } + + TEMP3 = (D1 * SENS) / 2097152 - OFF; + + return (double)TEMP3 / 327680.0; +} + +void *MS5611Loop(void *some_void_ptr) +{ + struct TGPS *GPS; + int fd; + uint16_t fc[6]; + + GPS = (struct TGPS *)some_void_ptr; + + // Initialise BMP085 + if ((fd = open_i2c(MS5611_ADDRESS)) >= 0) + { + printf("MS6511 found OK\n"); + + // Reset device + ms5611WriteRegister(fd, MS5611_CMD_RESET); + + delay(100); + + ms5611ReadPROM(fd, fc); + + close(fd); + + while (1) + { + if ((fd = open_i2c(MS5611_ADDRESS)) >= 0) + { + GPS->BMP180Temperature = ms5611ReadTemperature(fd, fc, 1); + GPS->Pressure = ms5611ReadPressure(fd, fc, GPS->BMP180Temperature); + + printf("**** Temperature is %5.2lf\n", GPS->BMP180Temperature); + printf("**** Pressure is %5.2lf\n", GPS->Pressure); + + close(fd); + } + + sleep(10); + } + } + return 0; +} + diff --git a/tracker/MS5611.h b/tracker/MS5611.h new file mode 100755 index 0000000..0f695a3 --- /dev/null +++ b/tracker/MS5611.h @@ -0,0 +1 @@ +void *MS5611Loop(void *some_void_ptr); diff --git a/tracker/aprs.c b/tracker/aprs.c index c77b647..81beae3 100755 --- a/tracker/aprs.c +++ b/tracker/aprs.c @@ -360,7 +360,8 @@ void SendAPRS(struct TGPS *GPS) { char s[10]; - sprintf(s, strncpy(s, Config.APRS_Callsign, 7)); + strncpy(s, Config.APRS_Callsign, 7); + s[7] = '\0'; if(Config.APRS_ID) snprintf(s + strlen(s), 4, "-%i", Config.APRS_ID); // Transmit telemetry definitions diff --git a/tracker/cmp.c b/tracker/cmp.c new file mode 100755 index 0000000..1d39b94 --- /dev/null +++ b/tracker/cmp.c @@ -0,0 +1,2665 @@ +#include +#include +#include + +#include "cmp.h" + +static const uint32_t version = 10; +static const uint32_t mp_version = 5; + +enum { + POSITIVE_FIXNUM_MARKER = 0x00, + FIXMAP_MARKER = 0x80, + FIXARRAY_MARKER = 0x90, + FIXSTR_MARKER = 0xA0, + NIL_MARKER = 0xC0, + FALSE_MARKER = 0xC2, + TRUE_MARKER = 0xC3, + BIN8_MARKER = 0xC4, + BIN16_MARKER = 0xC5, + BIN32_MARKER = 0xC6, + EXT8_MARKER = 0xC7, + EXT16_MARKER = 0xC8, + EXT32_MARKER = 0xC9, + FLOAT_MARKER = 0xCA, + DOUBLE_MARKER = 0xCB, + U8_MARKER = 0xCC, + U16_MARKER = 0xCD, + U32_MARKER = 0xCE, + U64_MARKER = 0xCF, + S8_MARKER = 0xD0, + S16_MARKER = 0xD1, + S32_MARKER = 0xD2, + S64_MARKER = 0xD3, + FIXEXT1_MARKER = 0xD4, + FIXEXT2_MARKER = 0xD5, + FIXEXT4_MARKER = 0xD6, + FIXEXT8_MARKER = 0xD7, + FIXEXT16_MARKER = 0xD8, + STR8_MARKER = 0xD9, + STR16_MARKER = 0xDA, + STR32_MARKER = 0xDB, + ARRAY16_MARKER = 0xDC, + ARRAY32_MARKER = 0xDD, + MAP16_MARKER = 0xDE, + MAP32_MARKER = 0xDF, + NEGATIVE_FIXNUM_MARKER = 0xE0 +}; + +enum { + FIXARRAY_SIZE = 0xF, + FIXMAP_SIZE = 0xF, + FIXSTR_SIZE = 0x1F +}; + +enum { + ERROR_NONE, + STR_DATA_LENGTH_TOO_LONG_ERROR, + BIN_DATA_LENGTH_TOO_LONG_ERROR, + ARRAY_LENGTH_TOO_LONG_ERROR, + MAP_LENGTH_TOO_LONG_ERROR, + INPUT_VALUE_TOO_LARGE_ERROR, + FIXED_VALUE_WRITING_ERROR, + TYPE_MARKER_READING_ERROR, + TYPE_MARKER_WRITING_ERROR, + DATA_READING_ERROR, + DATA_WRITING_ERROR, + EXT_TYPE_READING_ERROR, + EXT_TYPE_WRITING_ERROR, + INVALID_TYPE_ERROR, + LENGTH_READING_ERROR, + LENGTH_WRITING_ERROR, + ERROR_MAX +}; + +const char *cmp_error_messages[ERROR_MAX + 1] = { + "No Error", + "Specified string data length is too long (> 0xFFFFFFFF)", + "Specified binary data length is too long (> 0xFFFFFFFF)", + "Specified array length is too long (> 0xFFFFFFFF)", + "Specified map length is too long (> 0xFFFFFFFF)", + "Input value is too large", + "Error writing fixed value", + "Error reading type marker", + "Error writing type marker", + "Error reading packed data", + "Error writing packed data", + "Error reading ext type", + "Error writing ext type", + "Invalid type", + "Error reading size", + "Error writing size", + "Max Error" +}; + +static const int32_t _i = 1; +#define is_bigendian() ((*(char *)&_i) == 0) + +static uint16_t be16(uint16_t x) { + char *b = (char *)&x; + + if (!is_bigendian()) { + char swap = 0; + + swap = b[0]; + b[0] = b[1]; + b[1] = swap; + } + + return x; +} + +static uint32_t be32(uint32_t x) { + char *b = (char *)&x; + + if (!is_bigendian()) { + char swap = 0; + + swap = b[0]; + b[0] = b[3]; + b[3] = swap; + + swap = b[1]; + b[1] = b[2]; + b[2] = swap; + } + + return x; +} + +static uint64_t be64(uint64_t x) { + char *b = (char *)&x; + + if (!is_bigendian()) { + char swap = 0; + + swap = b[0]; + b[0] = b[7]; + b[7] = swap; + + swap = b[1]; + b[1] = b[6]; + b[6] = swap; + + swap = b[2]; + b[2] = b[5]; + b[5] = swap; + + swap = b[3]; + b[3] = b[4]; + b[4] = swap; + } + + return x; +} + +static float befloat(float x) { + char *b = (char *)&x; + + if (!is_bigendian()) { + char swap = 0; + + swap = b[0]; + b[0] = b[3]; + b[3] = swap; + + swap = b[1]; + b[1] = b[2]; + b[2] = swap; + } + + return x; +} + +static double bedouble(double x) { + char *b = (char *)&x; + + if (!is_bigendian()) { + char swap = 0; + + swap = b[0]; + b[0] = b[7]; + b[7] = swap; + + swap = b[1]; + b[1] = b[6]; + b[6] = swap; + + swap = b[2]; + b[2] = b[5]; + b[5] = swap; + + swap = b[3]; + b[3] = b[4]; + b[4] = swap; + } + + return x; +} + +static bool read_byte(cmp_ctx_t *ctx, uint8_t *x) { + return ctx->read(ctx, x, sizeof(uint8_t)); +} + +static bool write_byte(cmp_ctx_t *ctx, uint8_t x) { + return (ctx->write(ctx, &x, sizeof(uint8_t)) == (sizeof(uint8_t))); +} + +static bool read_type_marker(cmp_ctx_t *ctx, uint8_t *marker) { + if (read_byte(ctx, marker)) + return true; + + ctx->error = TYPE_MARKER_READING_ERROR; + return false; +} + +static bool write_type_marker(cmp_ctx_t *ctx, uint8_t marker) { + if (write_byte(ctx, marker)) + return true; + + ctx->error = TYPE_MARKER_WRITING_ERROR; + return false; +} + +static bool write_fixed_value(cmp_ctx_t *ctx, uint8_t value) { + if (write_byte(ctx, value)) + return true; + + ctx->error = FIXED_VALUE_WRITING_ERROR; + return false; +} + +void cmp_init(cmp_ctx_t *ctx, void *buf, cmp_reader read, cmp_writer write) { + ctx->error = ERROR_NONE; + ctx->buf = buf; + ctx->read = read; + ctx->write = write; +} + +uint32_t cmp_version(void) { + return version; +} + +uint32_t cmp_mp_version(void) { + return mp_version; +} + +const char* cmp_strerror(cmp_ctx_t *ctx) { + if (ctx->error > ERROR_NONE && ctx->error < ERROR_MAX) + return cmp_error_messages[ctx->error]; + + return ""; +} + +bool cmp_write_pfix(cmp_ctx_t *ctx, uint8_t c) { + if (c <= 0x7F) + return write_fixed_value(ctx, c); + + ctx->error = INPUT_VALUE_TOO_LARGE_ERROR; + return false; +} + +bool cmp_write_nfix(cmp_ctx_t *ctx, int8_t c) { + if (c >= -32 && c <= -1) + return write_fixed_value(ctx, c); + + ctx->error = INPUT_VALUE_TOO_LARGE_ERROR; + return false; +} + +bool cmp_write_sfix(cmp_ctx_t *ctx, int8_t c) { + if (c >= 0) + return cmp_write_pfix(ctx, c); + if (c >= -32 && c <= -1) + return cmp_write_nfix(ctx, c); + + ctx->error = INPUT_VALUE_TOO_LARGE_ERROR; + return false; +} + +bool cmp_write_s8(cmp_ctx_t *ctx, int8_t c) { + if (!write_type_marker(ctx, S8_MARKER)) + return false; + + return ctx->write(ctx, &c, sizeof(int8_t)); +} + +bool cmp_write_s16(cmp_ctx_t *ctx, int16_t s) { + if (!write_type_marker(ctx, S16_MARKER)) + return false; + + s = be16(s); + + return ctx->write(ctx, &s, sizeof(int16_t)); +} + +bool cmp_write_s32(cmp_ctx_t *ctx, int32_t i) { + if (!write_type_marker(ctx, S32_MARKER)) + return false; + + i = be32(i); + + return ctx->write(ctx, &i, sizeof(int32_t)); +} + +bool cmp_write_s64(cmp_ctx_t *ctx, int64_t l) { + if (!write_type_marker(ctx, S64_MARKER)) + return false; + + l = be64(l); + + return ctx->write(ctx, &l, sizeof(int64_t)); +} + +bool cmp_write_sint(cmp_ctx_t *ctx, int64_t d) { + if (d >= 0) + return cmp_write_uint(ctx, d); + if (d >= -32) + return cmp_write_nfix(ctx, d); + if (d >= -128) + return cmp_write_s8(ctx, d); + if (d >= -32768) + return cmp_write_s16(ctx, d); + if (d >= (-2147483647 - 1)) + return cmp_write_s32(ctx, d); + + return cmp_write_s64(ctx, d); +} + +bool cmp_write_ufix(cmp_ctx_t *ctx, uint8_t c) { + return cmp_write_pfix(ctx, c); +} + +bool cmp_write_u8(cmp_ctx_t *ctx, uint8_t c) { + if (!write_type_marker(ctx, U8_MARKER)) + return false; + + return ctx->write(ctx, &c, sizeof(uint8_t)); +} + +bool cmp_write_u16(cmp_ctx_t *ctx, uint16_t s) { + if (!write_type_marker(ctx, U16_MARKER)) + return false; + + s = be16(s); + + return ctx->write(ctx, &s, sizeof(uint16_t)); +} + +bool cmp_write_u32(cmp_ctx_t *ctx, uint32_t i) { + if (!write_type_marker(ctx, U32_MARKER)) + return false; + + i = be32(i); + + return ctx->write(ctx, &i, sizeof(uint32_t)); +} + +bool cmp_write_u64(cmp_ctx_t *ctx, uint64_t l) { + if (!write_type_marker(ctx, U64_MARKER)) + return false; + + l = be64(l); + + return ctx->write(ctx, &l, sizeof(uint64_t)); +} + +bool cmp_write_uint(cmp_ctx_t *ctx, uint64_t u) { + if (u <= 0x7F) + return cmp_write_pfix(ctx, u); + if (u <= 0xFF) + return cmp_write_u8(ctx, u); + if (u <= 0xFFFF) + return cmp_write_u16(ctx, u); + if (u <= 0xFFFFFFFF) + return cmp_write_u32(ctx, u); + + return cmp_write_u64(ctx, u); +} + +bool cmp_write_float(cmp_ctx_t *ctx, float f) { + if (!write_type_marker(ctx, FLOAT_MARKER)) + return false; + + f = befloat(f); + + return ctx->write(ctx, &f, sizeof(float)); +} + +bool cmp_write_double(cmp_ctx_t *ctx, double d) { + if (!write_type_marker(ctx, DOUBLE_MARKER)) + return false; + + d = bedouble(d); + + return ctx->write(ctx, &d, sizeof(double)); +} + +bool cmp_write_nil(cmp_ctx_t *ctx) { + return write_type_marker(ctx, NIL_MARKER); +} + +bool cmp_write_true(cmp_ctx_t *ctx) { + return write_type_marker(ctx, TRUE_MARKER); +} + +bool cmp_write_false(cmp_ctx_t *ctx) { + return write_type_marker(ctx, FALSE_MARKER); +} + +bool cmp_write_bool(cmp_ctx_t *ctx, bool b) { + if (b) + return cmp_write_true(ctx); + + return cmp_write_false(ctx); +} + +bool cmp_write_u8_as_bool(cmp_ctx_t *ctx, uint8_t b) { + if (b) + return cmp_write_true(ctx); + + return cmp_write_false(ctx); +} + +bool cmp_write_fixstr_marker(cmp_ctx_t *ctx, uint8_t size) { + if (size <= FIXSTR_SIZE) + return write_fixed_value(ctx, FIXSTR_MARKER | size); + + ctx->error = INPUT_VALUE_TOO_LARGE_ERROR; + return false; +} + +bool cmp_write_fixstr(cmp_ctx_t *ctx, const char *data, uint8_t size) { + if (!cmp_write_fixstr_marker(ctx, size)) + return false; + + if (size == 0) + return true; + + if (ctx->write(ctx, data, size)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_str8_marker(cmp_ctx_t *ctx, uint8_t size) { + if (!write_type_marker(ctx, STR8_MARKER)) + return false; + + if (ctx->write(ctx, &size, sizeof(uint8_t))) + return true; + + ctx->error = LENGTH_WRITING_ERROR; + return false; +} + +bool cmp_write_str8(cmp_ctx_t *ctx, const char *data, uint8_t size) { + if (!cmp_write_str8_marker(ctx, size)) + return false; + + if (size == 0) + return true; + + if (ctx->write(ctx, data, size)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_str16_marker(cmp_ctx_t *ctx, uint16_t size) { + if (!write_type_marker(ctx, STR16_MARKER)) + return false; + + size = be16(size); + + if (ctx->write(ctx, &size, sizeof(uint16_t))) + return true; + + ctx->error = LENGTH_WRITING_ERROR; + return false; +} + +bool cmp_write_str16(cmp_ctx_t *ctx, const char *data, uint16_t size) { + if (!cmp_write_str16_marker(ctx, size)) + return false; + + if (size == 0) + return true; + + if (ctx->write(ctx, data, size)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_str32_marker(cmp_ctx_t *ctx, uint32_t size) { + if (!write_type_marker(ctx, STR32_MARKER)) + return false; + + size = be32(size); + + if (ctx->write(ctx, &size, sizeof(uint32_t))) + return true; + + ctx->error = LENGTH_WRITING_ERROR; + return false; +} + +bool cmp_write_str32(cmp_ctx_t *ctx, const char *data, uint32_t size) { + if (!cmp_write_str32_marker(ctx, size)) + return false; + + if (size == 0) + return true; + + if (ctx->write(ctx, data, size)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_str_marker(cmp_ctx_t *ctx, uint32_t size) { + if (size <= FIXSTR_SIZE) + return cmp_write_fixstr_marker(ctx, size); + if (size <= 0xFF) + return cmp_write_str8_marker(ctx, size); + if (size <= 0xFFFF) + return cmp_write_str16_marker(ctx, size); + + return cmp_write_str32_marker(ctx, size); +} + +bool cmp_write_str(cmp_ctx_t *ctx, const char *data, uint32_t size) { + if (size <= FIXSTR_SIZE) + return cmp_write_fixstr(ctx, data, size); + if (size <= 0xFF) + return cmp_write_str8(ctx, data, size); + if (size <= 0xFFFF) + return cmp_write_str16(ctx, data, size); + + return cmp_write_str32(ctx, data, size); +} + +bool cmp_write_bin8_marker(cmp_ctx_t *ctx, uint8_t size) { + if (!write_type_marker(ctx, BIN8_MARKER)) + return false; + + if (ctx->write(ctx, &size, sizeof(uint8_t))) + return true; + + ctx->error = LENGTH_WRITING_ERROR; + return false; +} + +bool cmp_write_bin8(cmp_ctx_t *ctx, const void *data, uint8_t size) { + if (!cmp_write_bin8_marker(ctx, size)) + return false; + + if (size == 0) + return true; + + if (ctx->write(ctx, data, size)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_bin16_marker(cmp_ctx_t *ctx, uint16_t size) { + if (!write_type_marker(ctx, BIN16_MARKER)) + return false; + + size = be16(size); + + if (ctx->write(ctx, &size, sizeof(uint16_t))) + return true; + + ctx->error = LENGTH_WRITING_ERROR; + return false; +} + +bool cmp_write_bin16(cmp_ctx_t *ctx, const void *data, uint16_t size) { + if (!cmp_write_bin16_marker(ctx, size)) + return false; + + if (size == 0) + return true; + + if (ctx->write(ctx, data, size)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_bin32_marker(cmp_ctx_t *ctx, uint32_t size) { + if (!write_type_marker(ctx, BIN32_MARKER)) + return false; + + size = be32(size); + + if (ctx->write(ctx, &size, sizeof(uint32_t))) + return true; + + ctx->error = LENGTH_WRITING_ERROR; + return false; +} + +bool cmp_write_bin32(cmp_ctx_t *ctx, const void *data, uint32_t size) { + if (!cmp_write_bin32_marker(ctx, size)) + return false; + + if (size == 0) + return true; + + if (ctx->write(ctx, data, size)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_bin_marker(cmp_ctx_t *ctx, uint32_t size) { + if (size <= 0xFF) + return cmp_write_bin8_marker(ctx, size); + if (size <= 0xFFFF) + return cmp_write_bin16_marker(ctx, size); + + return cmp_write_bin32_marker(ctx, size); +} + +bool cmp_write_bin(cmp_ctx_t *ctx, const void *data, uint32_t size) { + if (size <= 0xFF) + return cmp_write_bin8(ctx, data, size); + if (size <= 0xFFFF) + return cmp_write_bin16(ctx, data, size); + + return cmp_write_bin32(ctx, data, size); +} + +bool cmp_write_fixarray(cmp_ctx_t *ctx, uint8_t size) { + if (size <= FIXARRAY_SIZE) + return write_fixed_value(ctx, FIXARRAY_MARKER | size); + + ctx->error = INPUT_VALUE_TOO_LARGE_ERROR; + return false; +} + +bool cmp_write_array16(cmp_ctx_t *ctx, uint16_t size) { + if (!write_type_marker(ctx, ARRAY16_MARKER)) + return false; + + size = be16(size); + + if (ctx->write(ctx, &size, sizeof(uint16_t))) + return true; + + ctx->error = LENGTH_WRITING_ERROR; + return false; +} + +bool cmp_write_array32(cmp_ctx_t *ctx, uint32_t size) { + if (!write_type_marker(ctx, ARRAY32_MARKER)) + return false; + + size = be32(size); + + if (ctx->write(ctx, &size, sizeof(uint32_t))) + return true; + + ctx->error = LENGTH_WRITING_ERROR; + return false; +} + +bool cmp_write_array(cmp_ctx_t *ctx, uint32_t size) { + if (size <= FIXARRAY_SIZE) + return cmp_write_fixarray(ctx, size); + if (size <= 0xFFFF) + return cmp_write_array16(ctx, size); + + return cmp_write_array32(ctx, size); +} + +bool cmp_write_fixmap(cmp_ctx_t *ctx, uint8_t size) { + if (size <= FIXMAP_SIZE) + return write_fixed_value(ctx, FIXMAP_MARKER | size); + + ctx->error = INPUT_VALUE_TOO_LARGE_ERROR; + return false; +} + +bool cmp_write_map16(cmp_ctx_t *ctx, uint16_t size) { + if (!write_type_marker(ctx, MAP16_MARKER)) + return false; + + size = be16(size); + + if (ctx->write(ctx, &size, sizeof(uint16_t))) + return true; + + ctx->error = LENGTH_WRITING_ERROR; + return false; +} + +bool cmp_write_map32(cmp_ctx_t *ctx, uint32_t size) { + if (!write_type_marker(ctx, MAP32_MARKER)) + return false; + + size = be32(size); + + if (ctx->write(ctx, &size, sizeof(uint32_t))) + return true; + + ctx->error = LENGTH_WRITING_ERROR; + return false; +} + +bool cmp_write_map(cmp_ctx_t *ctx, uint32_t size) { + if (size <= FIXMAP_SIZE) + return cmp_write_fixmap(ctx, size); + if (size <= 0xFFFF) + return cmp_write_map16(ctx, size); + + return cmp_write_map32(ctx, size); +} + +bool cmp_write_fixext1_marker(cmp_ctx_t *ctx, int8_t type) { + if (!write_type_marker(ctx, FIXEXT1_MARKER)) + return false; + + if (ctx->write(ctx, &type, sizeof(int8_t))) + return true; + + ctx->error = EXT_TYPE_WRITING_ERROR; + return false; +} + +bool cmp_write_fixext1(cmp_ctx_t *ctx, int8_t type, const void *data) { + if (!cmp_write_fixext1_marker(ctx, type)) + return false; + + if (ctx->write(ctx, data, 1)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_fixext2_marker(cmp_ctx_t *ctx, int8_t type) { + if (!write_type_marker(ctx, FIXEXT2_MARKER)) + return false; + + if (ctx->write(ctx, &type, sizeof(int8_t))) + return true; + + ctx->error = EXT_TYPE_WRITING_ERROR; + return false; +} + +bool cmp_write_fixext2(cmp_ctx_t *ctx, int8_t type, const void *data) { + if (!cmp_write_fixext2_marker(ctx, type)) + return false; + + if (ctx->write(ctx, data, 2)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_fixext4_marker(cmp_ctx_t *ctx, int8_t type) { + if (!write_type_marker(ctx, FIXEXT4_MARKER)) + return false; + + if (ctx->write(ctx, &type, sizeof(int8_t))) + return true; + + ctx->error = EXT_TYPE_WRITING_ERROR; + return false; +} + +bool cmp_write_fixext4(cmp_ctx_t *ctx, int8_t type, const void *data) { + if (!cmp_write_fixext4_marker(ctx, type)) + return false; + + if (ctx->write(ctx, data, 4)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_fixext8_marker(cmp_ctx_t *ctx, int8_t type) { + if (!write_type_marker(ctx, FIXEXT8_MARKER)) + return false; + + if (ctx->write(ctx, &type, sizeof(int8_t))) + return true; + + ctx->error = EXT_TYPE_WRITING_ERROR; + return false; +} + +bool cmp_write_fixext8(cmp_ctx_t *ctx, int8_t type, const void *data) { + if (!cmp_write_fixext8_marker(ctx, type)) + return false; + + if (ctx->write(ctx, data, 8)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_fixext16_marker(cmp_ctx_t *ctx, int8_t type) { + if (!write_type_marker(ctx, FIXEXT16_MARKER)) + return false; + + if (ctx->write(ctx, &type, sizeof(int8_t))) + return true; + + ctx->error = EXT_TYPE_WRITING_ERROR; + return false; +} + +bool cmp_write_fixext16(cmp_ctx_t *ctx, int8_t type, const void *data) { + if (!cmp_write_fixext16_marker(ctx, type)) + return false; + + if (ctx->write(ctx, data, 16)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_ext8_marker(cmp_ctx_t *ctx, int8_t type, uint8_t size) { + if (!write_type_marker(ctx, EXT8_MARKER)) + return false; + + if (!ctx->write(ctx, &size, sizeof(uint8_t))) { + ctx->error = LENGTH_WRITING_ERROR; + return false; + } + + if (ctx->write(ctx, &type, sizeof(int8_t))) + return true; + + ctx->error = EXT_TYPE_WRITING_ERROR; + return false; +} + +bool cmp_write_ext8(cmp_ctx_t *ctx, int8_t tp, uint8_t sz, const void *data) { + if (!cmp_write_ext8_marker(ctx, tp, sz)) + return false; + + if (ctx->write(ctx, data, sz)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_ext16_marker(cmp_ctx_t *ctx, int8_t type, uint16_t size) { + if (!write_type_marker(ctx, EXT16_MARKER)) + return false; + + size = be16(size); + + if (!ctx->write(ctx, &size, sizeof(uint16_t))) { + ctx->error = LENGTH_WRITING_ERROR; + return false; + } + + if (ctx->write(ctx, &type, sizeof(int8_t))) + return true; + + ctx->error = EXT_TYPE_WRITING_ERROR; + return false; +} + +bool cmp_write_ext16(cmp_ctx_t *ctx, int8_t tp, uint16_t sz, const void *data) { + if (!cmp_write_ext16_marker(ctx, tp, sz)) + return false; + + if (ctx->write(ctx, data, sz)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_ext32_marker(cmp_ctx_t *ctx, int8_t type, uint32_t size) { + if (!write_type_marker(ctx, EXT32_MARKER)) + return false; + + size = be32(size); + + if (!ctx->write(ctx, &size, sizeof(uint32_t))) { + ctx->error = LENGTH_WRITING_ERROR; + return false; + } + + if (ctx->write(ctx, &type, sizeof(int8_t))) + return true; + + ctx->error = EXT_TYPE_WRITING_ERROR; + return false; +} + +bool cmp_write_ext32(cmp_ctx_t *ctx, int8_t tp, uint32_t sz, const void *data) { + if (!cmp_write_ext32_marker(ctx, tp, sz)) + return false; + + if (ctx->write(ctx, data, sz)) + return true; + + ctx->error = DATA_WRITING_ERROR; + return false; +} + +bool cmp_write_ext_marker(cmp_ctx_t *ctx, int8_t tp, uint32_t sz) { + if (sz == 1) + return cmp_write_fixext1_marker(ctx, tp); + if (sz == 2) + return cmp_write_fixext2_marker(ctx, tp); + if (sz == 4) + return cmp_write_fixext4_marker(ctx, tp); + if (sz == 8) + return cmp_write_fixext8_marker(ctx, tp); + if (sz == 16) + return cmp_write_fixext16_marker(ctx, tp); + if (sz <= 0xFF) + return cmp_write_ext8_marker(ctx, tp, sz); + if (sz <= 0xFFFF) + return cmp_write_ext16_marker(ctx, tp, sz); + + return cmp_write_ext32_marker(ctx, tp, sz); +} + +bool cmp_write_ext(cmp_ctx_t *ctx, int8_t tp, uint32_t sz, const void *data) { + if (sz == 1) + return cmp_write_fixext1(ctx, tp, data); + if (sz == 2) + return cmp_write_fixext2(ctx, tp, data); + if (sz == 4) + return cmp_write_fixext4(ctx, tp, data); + if (sz == 8) + return cmp_write_fixext8(ctx, tp, data); + if (sz == 16) + return cmp_write_fixext16(ctx, tp, data); + if (sz <= 0xFF) + return cmp_write_ext8(ctx, tp, sz, data); + if (sz <= 0xFFFF) + return cmp_write_ext16(ctx, tp, sz, data); + + return cmp_write_ext32(ctx, tp, sz, data); +} + +bool cmp_write_object(cmp_ctx_t *ctx, cmp_object_t *obj) { + switch(obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + return cmp_write_pfix(ctx, obj->as.u8); + case CMP_TYPE_FIXMAP: + return cmp_write_fixmap(ctx, obj->as.map_size); + case CMP_TYPE_FIXARRAY: + return cmp_write_fixarray(ctx, obj->as.array_size); + case CMP_TYPE_FIXSTR: + return cmp_write_fixstr_marker(ctx, obj->as.str_size); + case CMP_TYPE_NIL: + return cmp_write_nil(ctx); + case CMP_TYPE_BOOLEAN: + if (obj->as.boolean) + return cmp_write_true(ctx); + return cmp_write_false(ctx); + case CMP_TYPE_BIN8: + return cmp_write_bin8_marker(ctx, obj->as.bin_size); + case CMP_TYPE_BIN16: + return cmp_write_bin16_marker(ctx, obj->as.bin_size); + case CMP_TYPE_BIN32: + return cmp_write_bin32_marker(ctx, obj->as.bin_size); + case CMP_TYPE_EXT8: + return cmp_write_ext8_marker(ctx, obj->as.ext.type, obj->as.ext.size); + case CMP_TYPE_EXT16: + return cmp_write_ext16_marker(ctx, obj->as.ext.type, obj->as.ext.size); + case CMP_TYPE_EXT32: + return cmp_write_ext32_marker(ctx, obj->as.ext.type, obj->as.ext.size); + case CMP_TYPE_FLOAT: + return cmp_write_float(ctx, obj->as.flt); + case CMP_TYPE_DOUBLE: + return cmp_write_double(ctx, obj->as.dbl); + case CMP_TYPE_UINT8: + return cmp_write_u8(ctx, obj->as.u8); + case CMP_TYPE_UINT16: + return cmp_write_u16(ctx, obj->as.u16); + case CMP_TYPE_UINT32: + return cmp_write_u32(ctx, obj->as.u32); + case CMP_TYPE_UINT64: + return cmp_write_u64(ctx, obj->as.u64); + case CMP_TYPE_SINT8: + return cmp_write_s8(ctx, obj->as.s8); + case CMP_TYPE_SINT16: + return cmp_write_s16(ctx, obj->as.s16); + case CMP_TYPE_SINT32: + return cmp_write_s32(ctx, obj->as.s32); + case CMP_TYPE_SINT64: + return cmp_write_s64(ctx, obj->as.s64); + case CMP_TYPE_FIXEXT1: + return cmp_write_fixext1_marker(ctx, obj->as.ext.type); + case CMP_TYPE_FIXEXT2: + return cmp_write_fixext2_marker(ctx, obj->as.ext.type); + case CMP_TYPE_FIXEXT4: + return cmp_write_fixext4_marker(ctx, obj->as.ext.type); + case CMP_TYPE_FIXEXT8: + return cmp_write_fixext8_marker(ctx, obj->as.ext.type); + case CMP_TYPE_FIXEXT16: + return cmp_write_fixext16_marker(ctx, obj->as.ext.type); + case CMP_TYPE_STR8: + return cmp_write_str8_marker(ctx, obj->as.str_size); + case CMP_TYPE_STR16: + return cmp_write_str16_marker(ctx, obj->as.str_size); + case CMP_TYPE_STR32: + return cmp_write_str32_marker(ctx, obj->as.str_size); + case CMP_TYPE_ARRAY16: + return cmp_write_array16(ctx, obj->as.array_size); + case CMP_TYPE_ARRAY32: + return cmp_write_array32(ctx, obj->as.array_size); + case CMP_TYPE_MAP16: + return cmp_write_map16(ctx, obj->as.map_size); + case CMP_TYPE_MAP32: + return cmp_write_map32(ctx, obj->as.map_size); + case CMP_TYPE_NEGATIVE_FIXNUM: + return cmp_write_nfix(ctx, obj->as.s8); + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_read_pfix(cmp_ctx_t *ctx, uint8_t *c) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_POSITIVE_FIXNUM) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *c = obj.as.u8; + return true; +} + +bool cmp_read_nfix(cmp_ctx_t *ctx, int8_t *c) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_NEGATIVE_FIXNUM) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *c = obj.as.s8; + return true; +} + +bool cmp_read_sfix(cmp_ctx_t *ctx, int8_t *c) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_NEGATIVE_FIXNUM: + *c = obj.as.s8; + return true; + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_read_s8(cmp_ctx_t *ctx, int8_t *c) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_SINT8) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *c = obj.as.s8; + return true; +} + +bool cmp_read_s16(cmp_ctx_t *ctx, int16_t *s) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_SINT16) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *s = obj.as.s16; + return true; +} + +bool cmp_read_s32(cmp_ctx_t *ctx, int32_t *i) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_SINT32) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *i = obj.as.s32; + return true; +} + +bool cmp_read_s64(cmp_ctx_t *ctx, int64_t *l) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_SINT64) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *l = obj.as.s64; + return true; +} + +bool cmp_read_char(cmp_ctx_t *ctx, int8_t *c) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + *c = obj.as.s8; + return true; + case CMP_TYPE_UINT8: + if (obj.as.u8 <= 127) { + *c = obj.as.u8; + return true; + } + else { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_read_short(cmp_ctx_t *ctx, int16_t *s) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + *s = obj.as.s8; + return true; + case CMP_TYPE_UINT8: + *s = obj.as.u8; + return true; + case CMP_TYPE_SINT16: + *s = obj.as.s16; + return true; + case CMP_TYPE_UINT16: + if (obj.as.u16 <= 32767) { + *s = obj.as.u16; + return true; + } + else { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_read_int(cmp_ctx_t *ctx, int32_t *i) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + *i = obj.as.s8; + return true; + case CMP_TYPE_UINT8: + *i = obj.as.u8; + return true; + case CMP_TYPE_SINT16: + *i = obj.as.s16; + return true; + case CMP_TYPE_UINT16: + *i = obj.as.u16; + return true; + case CMP_TYPE_SINT32: + *i = obj.as.s32; + return true; + case CMP_TYPE_UINT32: + if (obj.as.u32 <= 2147483647) { + *i = obj.as.u32; + return true; + } + else { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_read_long(cmp_ctx_t *ctx, int64_t *d) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + *d = obj.as.s8; + return true; + case CMP_TYPE_UINT8: + *d = obj.as.u8; + return true; + case CMP_TYPE_SINT16: + *d = obj.as.s16; + return true; + case CMP_TYPE_UINT16: + *d = obj.as.u16; + return true; + case CMP_TYPE_SINT32: + *d = obj.as.s32; + return true; + case CMP_TYPE_UINT32: + *d = obj.as.u32; + return true; + case CMP_TYPE_SINT64: + *d = obj.as.s64; + return true; + case CMP_TYPE_UINT64: + if (obj.as.u64 <= 9223372036854775807) { + *d = obj.as.u64; + return true; + } + else { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_read_sinteger(cmp_ctx_t *ctx, int64_t *d) { + return cmp_read_long(ctx, d); +} + +bool cmp_read_ufix(cmp_ctx_t *ctx, uint8_t *c) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_NEGATIVE_FIXNUM) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *c = obj.as.u8; + return true; +} + +bool cmp_read_u8(cmp_ctx_t *ctx, uint8_t *c) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_UINT8) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *c = obj.as.u8; + return true; +} + +bool cmp_read_u16(cmp_ctx_t *ctx, uint16_t *s) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_UINT16) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *s = obj.as.u16; + return true; +} + +bool cmp_read_u32(cmp_ctx_t *ctx, uint32_t *i) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_UINT32) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *i = obj.as.u32; + return true; +} + +bool cmp_read_u64(cmp_ctx_t *ctx, uint64_t *l) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_UINT64) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *l = obj.as.u64; + return true; +} + +bool cmp_read_uchar(cmp_ctx_t *ctx, uint8_t *c) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + *c = obj.as.u8; + return true; + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_read_ushort(cmp_ctx_t *ctx, uint16_t *s) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + *s = obj.as.u8; + return true; + case CMP_TYPE_UINT16: + *s = obj.as.u16; + return true; + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_read_uint(cmp_ctx_t *ctx, uint32_t *i) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + *i = obj.as.u8; + return true; + case CMP_TYPE_UINT16: + *i = obj.as.u16; + return true; + case CMP_TYPE_UINT32: + *i = obj.as.u32; + return true; + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_read_ulong(cmp_ctx_t *ctx, uint64_t *u) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + *u = obj.as.u8; + return true; + case CMP_TYPE_UINT16: + *u = obj.as.u16; + return true; + case CMP_TYPE_UINT32: + *u = obj.as.u32; + return true; + case CMP_TYPE_UINT64: + *u = obj.as.u64; + return true; + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_read_uinteger(cmp_ctx_t *ctx, uint64_t *d) { + return cmp_read_ulong(ctx, d); +} + +bool cmp_read_float(cmp_ctx_t *ctx, float *f) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_FLOAT) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *f = obj.as.flt; + + return true; +} + +bool cmp_read_double(cmp_ctx_t *ctx, double *d) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_DOUBLE) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *d = obj.as.dbl; + + return true; +} + +bool cmp_read_nil(cmp_ctx_t *ctx) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type == CMP_TYPE_NIL) + return true; + + ctx->error = INVALID_TYPE_ERROR; + return false; +} + +bool cmp_read_bool(cmp_ctx_t *ctx, bool *b) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_BOOLEAN) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + if (obj.as.boolean) + *b = true; + else + *b = false; + + return true; +} + +bool cmp_read_bool_as_u8(cmp_ctx_t *ctx, uint8_t *b) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_BOOLEAN) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + if (obj.as.boolean) + *b = 1; + else + *b = 0; + + return true; +} + +bool cmp_read_str_size(cmp_ctx_t *ctx, uint32_t *size) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_FIXSTR: + case CMP_TYPE_STR8: + case CMP_TYPE_STR16: + case CMP_TYPE_STR32: + *size = obj.as.str_size; + return true; + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_read_str(cmp_ctx_t *ctx, char *data, uint32_t *size) { + uint32_t str_size = 0; + + if (!cmp_read_str_size(ctx, &str_size)) + return false; + + if ((str_size + 1) > *size) { + *size = str_size; + ctx->error = STR_DATA_LENGTH_TOO_LONG_ERROR; + return false; + } + + if (!ctx->read(ctx, data, str_size)) { + ctx->error = DATA_READING_ERROR; + return false; + } + + data[str_size] = 0; + + *size = str_size; + return true; +} + +bool cmp_read_bin_size(cmp_ctx_t *ctx, uint32_t *size) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_BIN8: + case CMP_TYPE_BIN16: + case CMP_TYPE_BIN32: + *size = obj.as.bin_size; + return true; + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_read_bin(cmp_ctx_t *ctx, void *data, uint32_t *size) { + uint32_t bin_size = 0; + + if (!cmp_read_bin_size(ctx, &bin_size)) + return false; + + if (bin_size > *size) { + ctx->error = BIN_DATA_LENGTH_TOO_LONG_ERROR; + return false; + } + + if (!ctx->read(ctx, data, bin_size)) { + ctx->error = DATA_READING_ERROR; + return false; + } + + *size = bin_size; + return true; +} + +bool cmp_read_array(cmp_ctx_t *ctx, uint32_t *size) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_FIXARRAY: + case CMP_TYPE_ARRAY16: + case CMP_TYPE_ARRAY32: + *size = obj.as.array_size; + return true; + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_read_map(cmp_ctx_t *ctx, uint32_t *size) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_FIXMAP: + case CMP_TYPE_MAP16: + case CMP_TYPE_MAP32: + *size = obj.as.map_size; + return true; + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_read_fixext1_marker(cmp_ctx_t *ctx, int8_t *type) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_FIXEXT1) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *type = obj.as.ext.type; + return true; +} + +bool cmp_read_fixext1(cmp_ctx_t *ctx, int8_t *type, void *data) { + if (!cmp_read_fixext1_marker(ctx, type)) + return false; + + if (ctx->read(ctx, data, 1)) + return true; + + ctx->error = DATA_READING_ERROR; + return false; +} + +bool cmp_read_fixext2_marker(cmp_ctx_t *ctx, int8_t *type) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_FIXEXT2) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *type = obj.as.ext.type; + return true; +} + +bool cmp_read_fixext2(cmp_ctx_t *ctx, int8_t *type, void *data) { + if (!cmp_read_fixext2_marker(ctx, type)) + return false; + + if (ctx->read(ctx, data, 2)) + return true; + + ctx->error = DATA_READING_ERROR; + return false; +} + +bool cmp_read_fixext4_marker(cmp_ctx_t *ctx, int8_t *type) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_FIXEXT4) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *type = obj.as.ext.type; + return true; +} + +bool cmp_read_fixext4(cmp_ctx_t *ctx, int8_t *type, void *data) { + if (!cmp_read_fixext4_marker(ctx, type)) + return false; + + if (ctx->read(ctx, data, 4)) + return true; + + ctx->error = DATA_READING_ERROR; + return false; +} + +bool cmp_read_fixext8_marker(cmp_ctx_t *ctx, int8_t *type) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_FIXEXT8) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *type = obj.as.ext.type; + return true; +} + +bool cmp_read_fixext8(cmp_ctx_t *ctx, int8_t *type, void *data) { + if (!cmp_read_fixext8_marker(ctx, type)) + return false; + + if (ctx->read(ctx, data, 8)) + return true; + + ctx->error = DATA_READING_ERROR; + return false; +} + +bool cmp_read_fixext16_marker(cmp_ctx_t *ctx, int8_t *type) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_FIXEXT16) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *type = obj.as.ext.type; + return true; +} + +bool cmp_read_fixext16(cmp_ctx_t *ctx, int8_t *type, void *data) { + if (!cmp_read_fixext16_marker(ctx, type)) + return false; + + if (ctx->read(ctx, data, 16)) + return true; + + ctx->error = DATA_READING_ERROR; + return false; +} + +bool cmp_read_ext8_marker(cmp_ctx_t *ctx, int8_t *type, uint8_t *size) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_EXT8) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *type = obj.as.ext.type; + *size = obj.as.ext.size; + + return true; +} + +bool cmp_read_ext8(cmp_ctx_t *ctx, int8_t *type, uint8_t *size, void *data) { + if (!cmp_read_ext8_marker(ctx, type, size)) + return false; + + if (ctx->read(ctx, data, *size)) + return true; + + ctx->error = DATA_READING_ERROR; + return false; +} + +bool cmp_read_ext16_marker(cmp_ctx_t *ctx, int8_t *type, uint16_t *size) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_EXT16) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *type = obj.as.ext.type; + *size = obj.as.ext.size; + + return true; +} + +bool cmp_read_ext16(cmp_ctx_t *ctx, int8_t *type, uint16_t *size, void *data) { + if (!cmp_read_ext16_marker(ctx, type, size)) + return false; + + if (ctx->read(ctx, data, *size)) + return true; + + ctx->error = DATA_READING_ERROR; + return false; +} + +bool cmp_read_ext32_marker(cmp_ctx_t *ctx, int8_t *type, uint32_t *size) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + if (obj.type != CMP_TYPE_EXT32) { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + *type = obj.as.ext.type; + *size = obj.as.ext.size; + + return true; +} + +bool cmp_read_ext32(cmp_ctx_t *ctx, int8_t *type, uint32_t *size, void *data) { + if (!cmp_read_ext32_marker(ctx, type, size)) + return false; + + if (ctx->read(ctx, data, *size)) + return true; + + ctx->error = DATA_READING_ERROR; + return false; +} + +bool cmp_read_ext_marker(cmp_ctx_t *ctx, int8_t *type, uint32_t *size) { + cmp_object_t obj; + + if (!cmp_read_object(ctx, &obj)) + return false; + + switch (obj.type) { + case CMP_TYPE_FIXEXT1: + case CMP_TYPE_FIXEXT2: + case CMP_TYPE_FIXEXT4: + case CMP_TYPE_FIXEXT8: + case CMP_TYPE_FIXEXT16: + case CMP_TYPE_EXT8: + case CMP_TYPE_EXT16: + case CMP_TYPE_EXT32: + *type = obj.as.ext.type; + *size = obj.as.ext.size; + return true; + default: + ctx->error = INVALID_TYPE_ERROR; + return false; + } +} + +bool cmp_read_ext(cmp_ctx_t *ctx, int8_t *type, uint32_t *size, void *data) { + if (!cmp_read_ext_marker(ctx, type, size)) + return false; + + if (ctx->read(ctx, data, *size)) + return true; + + ctx->error = DATA_READING_ERROR; + return false; +} + +bool cmp_read_object(cmp_ctx_t *ctx, cmp_object_t *obj) { + uint8_t type_marker = 0; + + if (!read_type_marker(ctx, &type_marker)) + return false; + + if (type_marker <= 0x7F) { + obj->type = CMP_TYPE_POSITIVE_FIXNUM; + obj->as.u8 = type_marker; + } + else if (type_marker <= 0x8F) { + obj->type = CMP_TYPE_FIXMAP; + obj->as.map_size = type_marker & FIXMAP_SIZE; + } + else if (type_marker <= 0x9F) { + obj->type = CMP_TYPE_FIXARRAY; + obj->as.array_size = type_marker & FIXARRAY_SIZE; + } + else if (type_marker <= 0xBF) { + obj->type = CMP_TYPE_FIXSTR; + obj->as.str_size = type_marker & FIXSTR_SIZE; + } + else if (type_marker == NIL_MARKER) { + obj->type = CMP_TYPE_NIL; + obj->as.u8 = 0; + } + else if (type_marker == FALSE_MARKER) { + obj->type = CMP_TYPE_BOOLEAN; + obj->as.boolean = false; + } + else if (type_marker == TRUE_MARKER) { + obj->type = CMP_TYPE_BOOLEAN; + obj->as.boolean = true; + } + else if (type_marker == BIN8_MARKER) { + obj->type = CMP_TYPE_BIN8; + if (!ctx->read(ctx, &obj->as.u8, sizeof(uint8_t))) { + ctx->error = LENGTH_READING_ERROR; + return false; + } + obj->as.bin_size = obj->as.u8; + } + else if (type_marker == BIN16_MARKER) { + obj->type = CMP_TYPE_BIN16; + if (!ctx->read(ctx, &obj->as.u16, sizeof(uint16_t))) { + ctx->error = LENGTH_READING_ERROR; + return false; + } + obj->as.bin_size = be16(obj->as.u16); + } + else if (type_marker == BIN32_MARKER) { + obj->type = CMP_TYPE_BIN32; + if (!ctx->read(ctx, &obj->as.u32, sizeof(uint32_t))) { + ctx->error = LENGTH_READING_ERROR; + return false; + } + obj->as.bin_size = be32(obj->as.u32); + } + else if (type_marker == EXT8_MARKER) { + uint8_t ext_size; + int8_t ext_type; + + obj->type = CMP_TYPE_EXT8; + if (!ctx->read(ctx, &ext_size, sizeof(uint8_t))) { + ctx->error = LENGTH_READING_ERROR; + return false; + } + if (!ctx->read(ctx, &ext_type, sizeof(int8_t))) { + ctx->error = EXT_TYPE_READING_ERROR; + return false; + } + obj->as.ext.size = ext_size; + obj->as.ext.type = ext_type; + } + else if (type_marker == EXT16_MARKER) { + int8_t ext_type; + uint16_t ext_size; + + obj->type = CMP_TYPE_EXT16; + if (!ctx->read(ctx, &ext_size, sizeof(uint16_t))) { + ctx->error = LENGTH_READING_ERROR; + return false; + } + if (!ctx->read(ctx, &ext_type, sizeof(int8_t))) { + ctx->error = EXT_TYPE_READING_ERROR; + return false; + } + obj->as.ext.size = be16(ext_size); + obj->as.ext.type = ext_type; + } + else if (type_marker == EXT32_MARKER) { + int8_t ext_type; + uint32_t ext_size; + + obj->type = CMP_TYPE_EXT32; + if (!ctx->read(ctx, &ext_size, sizeof(uint32_t))) { + ctx->error = LENGTH_READING_ERROR; + return false; + } + if (!ctx->read(ctx, &ext_type, sizeof(int8_t))) { + ctx->error = EXT_TYPE_READING_ERROR; + return false; + } + obj->as.ext.size = be32(ext_size); + obj->as.ext.type = ext_type; + } + else if (type_marker == FLOAT_MARKER) { + obj->type = CMP_TYPE_FLOAT; + if (!ctx->read(ctx, &obj->as.flt, sizeof(float))) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.flt = befloat(obj->as.flt); + } + else if (type_marker == DOUBLE_MARKER) { + obj->type = CMP_TYPE_DOUBLE; + if (!ctx->read(ctx, &obj->as.dbl, sizeof(double))) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.dbl = bedouble(obj->as.dbl); + } + else if (type_marker == U8_MARKER) { + obj->type = CMP_TYPE_UINT8; + if (!ctx->read(ctx, &obj->as.u8, sizeof(uint8_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + } + else if (type_marker == U16_MARKER) { + obj->type = CMP_TYPE_UINT16; + if (!ctx->read(ctx, &obj->as.u16, sizeof(uint16_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.u16 = be16(obj->as.u16); + } + else if (type_marker == U32_MARKER) { + obj->type = CMP_TYPE_UINT32; + if (!ctx->read(ctx, &obj->as.u32, sizeof(uint32_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.u32 = be32(obj->as.u32); + } + else if (type_marker == U64_MARKER) { + obj->type = CMP_TYPE_UINT64; + if (!ctx->read(ctx, &obj->as.u64, sizeof(uint64_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.u64 = be64(obj->as.u64); + } + else if (type_marker == S8_MARKER) { + obj->type = CMP_TYPE_SINT8; + if (!ctx->read(ctx, &obj->as.s8, sizeof(int8_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + } + else if (type_marker == S16_MARKER) { + obj->type = CMP_TYPE_SINT16; + if (!ctx->read(ctx, &obj->as.s16, sizeof(int16_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.s16 = be16(obj->as.s16); + } + else if (type_marker == S32_MARKER) { + obj->type = CMP_TYPE_SINT32; + if (!ctx->read(ctx, &obj->as.s32, sizeof(int32_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.s32 = be32(obj->as.s32); + } + else if (type_marker == S64_MARKER) { + obj->type = CMP_TYPE_SINT64; + if (!ctx->read(ctx, &obj->as.s64, sizeof(int64_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.s64 = be64(obj->as.s64); + } + else if (type_marker == FIXEXT1_MARKER) { + obj->type = CMP_TYPE_FIXEXT1; + if (!ctx->read(ctx, &obj->as.ext.type, sizeof(int8_t))) { + ctx->error = EXT_TYPE_READING_ERROR; + return false; + } + obj->as.ext.size = 1; + } + else if (type_marker == FIXEXT2_MARKER) { + obj->type = CMP_TYPE_FIXEXT2; + if (!ctx->read(ctx, &obj->as.ext.type, sizeof(int8_t))) { + ctx->error = EXT_TYPE_READING_ERROR; + return false; + } + obj->as.ext.size = 2; + } + else if (type_marker == FIXEXT4_MARKER) { + obj->type = CMP_TYPE_FIXEXT4; + if (!ctx->read(ctx, &obj->as.ext.type, sizeof(int8_t))) { + ctx->error = EXT_TYPE_READING_ERROR; + return false; + } + obj->as.ext.size = 4; + } + else if (type_marker == FIXEXT8_MARKER) { + obj->type = CMP_TYPE_FIXEXT8; + if (!ctx->read(ctx, &obj->as.ext.type, sizeof(int8_t))) { + ctx->error = EXT_TYPE_READING_ERROR; + return false; + } + obj->as.ext.size = 8; + } + else if (type_marker == FIXEXT16_MARKER) { + obj->type = CMP_TYPE_FIXEXT16; + if (!ctx->read(ctx, &obj->as.ext.type, sizeof(int8_t))) { + ctx->error = EXT_TYPE_READING_ERROR; + return false; + } + obj->as.ext.size = 16; + } + else if (type_marker == STR8_MARKER) { + obj->type = CMP_TYPE_STR8; + if (!ctx->read(ctx, &obj->as.u8, sizeof(uint8_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.str_size = obj->as.u8; + } + else if (type_marker == STR16_MARKER) { + obj->type = CMP_TYPE_STR16; + if (!ctx->read(ctx, &obj->as.u16, sizeof(uint16_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.str_size = be16(obj->as.u16); + } + else if (type_marker == STR32_MARKER) { + obj->type = CMP_TYPE_STR32; + if (!ctx->read(ctx, &obj->as.u32, sizeof(uint32_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.str_size = be32(obj->as.u32); + } + else if (type_marker == ARRAY16_MARKER) { + obj->type = CMP_TYPE_ARRAY16; + if (!ctx->read(ctx, &obj->as.u16, sizeof(uint16_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.array_size = be16(obj->as.u16); + } + else if (type_marker == ARRAY32_MARKER) { + obj->type = CMP_TYPE_ARRAY32; + if (!ctx->read(ctx, &obj->as.u32, sizeof(uint32_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.array_size = be32(obj->as.u32); + } + else if (type_marker == MAP16_MARKER) { + obj->type = CMP_TYPE_MAP16; + if (!ctx->read(ctx, &obj->as.u16, sizeof(uint16_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.map_size = be16(obj->as.u16); + } + else if (type_marker == MAP32_MARKER) { + obj->type = CMP_TYPE_MAP32; + if (!ctx->read(ctx, &obj->as.u32, sizeof(uint32_t))) { + ctx->error = DATA_READING_ERROR; + return false; + } + obj->as.map_size = be32(obj->as.u32); + } + else if (type_marker >= NEGATIVE_FIXNUM_MARKER) { + obj->type = CMP_TYPE_NEGATIVE_FIXNUM; + obj->as.s8 = type_marker; + } + else { + ctx->error = INVALID_TYPE_ERROR; + return false; + } + + return true; +} + +bool cmp_object_is_char(cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + return true; + default: + return false; + } +} + +bool cmp_object_is_short(cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + case CMP_TYPE_SINT16: + return true; + default: + return false; + } +} + +bool cmp_object_is_int(cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + case CMP_TYPE_SINT16: + case CMP_TYPE_SINT32: + return true; + default: + return false; + } +} + +bool cmp_object_is_long(cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + case CMP_TYPE_SINT16: + case CMP_TYPE_SINT32: + case CMP_TYPE_SINT64: + return true; + default: + return false; + } +} + +bool cmp_object_is_sinteger(cmp_object_t *obj) { + return cmp_object_is_long(obj); +} + +bool cmp_object_is_uchar(cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + return true; + default: + return false; + } +} + +bool cmp_object_is_ushort(cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + return true; + case CMP_TYPE_UINT16: + return true; + default: + return false; + } +} + +bool cmp_object_is_uint(cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + case CMP_TYPE_UINT16: + case CMP_TYPE_UINT32: + return true; + default: + return false; + } +} + +bool cmp_object_is_ulong(cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + case CMP_TYPE_UINT16: + case CMP_TYPE_UINT32: + case CMP_TYPE_UINT64: + return true; + default: + return false; + } +} + +bool cmp_object_is_uinteger(cmp_object_t *obj) { + return cmp_object_is_ulong(obj); +} + +bool cmp_object_is_float(cmp_object_t *obj) { + if (obj->type == CMP_TYPE_FLOAT) + return true; + + return false; +} + +bool cmp_object_is_double(cmp_object_t *obj) { + if (obj->type == CMP_TYPE_DOUBLE) + return true; + + return false; +} + +bool cmp_object_is_nil(cmp_object_t *obj) { + if (obj->type == CMP_TYPE_NIL) + return true; + + return false; +} + +bool cmp_object_is_bool(cmp_object_t *obj) { + if (obj->type == CMP_TYPE_BOOLEAN) + return true; + + return false; +} + +bool cmp_object_is_str(cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_FIXSTR: + case CMP_TYPE_STR8: + case CMP_TYPE_STR16: + case CMP_TYPE_STR32: + return true; + default: + return false; + } +} + +bool cmp_object_is_bin(cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_BIN8: + case CMP_TYPE_BIN16: + case CMP_TYPE_BIN32: + return true; + default: + return false; + } +} + +bool cmp_object_is_array(cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_FIXARRAY: + case CMP_TYPE_ARRAY16: + case CMP_TYPE_ARRAY32: + return true; + default: + return false; + } +} + +bool cmp_object_is_map(cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_FIXMAP: + case CMP_TYPE_MAP16: + case CMP_TYPE_MAP32: + return true; + default: + return false; + } +} + +bool cmp_object_is_ext(cmp_object_t *obj) { + switch (obj->type) { + case CMP_TYPE_FIXEXT1: + case CMP_TYPE_FIXEXT2: + case CMP_TYPE_FIXEXT4: + case CMP_TYPE_FIXEXT8: + case CMP_TYPE_FIXEXT16: + case CMP_TYPE_EXT8: + case CMP_TYPE_EXT16: + case CMP_TYPE_EXT32: + return true; + default: + return false; + } +} + +bool cmp_object_as_char(cmp_object_t *obj, int8_t *c) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + *c = obj->as.s8; + return true; + case CMP_TYPE_UINT8: + if (obj->as.u8 <= 127) { + *c = obj->as.s8; + return true; + } + } + + return false; +} + +bool cmp_object_as_short(cmp_object_t *obj, int16_t *s) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + *s = obj->as.s8; + return true; + case CMP_TYPE_UINT8: + *s = obj->as.u8; + return true; + case CMP_TYPE_SINT16: + *s = obj->as.s16; + return true; + case CMP_TYPE_UINT16: + if (obj->as.u16 <= 32767) { + *s = obj->as.u16; + return true; + } + } + + return false; +} + +bool cmp_object_as_int(cmp_object_t *obj, int32_t *i) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + *i = obj->as.s8; + return true; + case CMP_TYPE_UINT8: + *i = obj->as.u8; + return true; + case CMP_TYPE_SINT16: + *i = obj->as.s16; + return true; + case CMP_TYPE_UINT16: + *i = obj->as.u16; + return true; + case CMP_TYPE_SINT32: + *i = obj->as.s32; + return true; + case CMP_TYPE_UINT32: + if (obj->as.u32 <= 2147483647) { + *i = obj->as.u32; + return true; + } + } + + return false; +} + +bool cmp_object_as_long(cmp_object_t *obj, int64_t *d) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_NEGATIVE_FIXNUM: + case CMP_TYPE_SINT8: + *d = obj->as.s8; + return true; + case CMP_TYPE_UINT8: + *d = obj->as.u8; + return true; + case CMP_TYPE_SINT16: + *d = obj->as.s16; + return true; + case CMP_TYPE_UINT16: + *d = obj->as.u16; + return true; + case CMP_TYPE_SINT32: + *d = obj->as.s32; + return true; + case CMP_TYPE_UINT32: + *d = obj->as.u32; + return true; + case CMP_TYPE_SINT64: + *d = obj->as.s64; + return true; + case CMP_TYPE_UINT64: + if (obj->as.u64 <= 9223372036854775807) { + *d = obj->as.u64; + return true; + } + } + + return false; +} + +bool cmp_object_as_sinteger(cmp_object_t *obj, int64_t *d) { + return cmp_object_as_long(obj, d); +} + +bool cmp_object_as_uchar(cmp_object_t *obj, uint8_t *c) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + *c = obj->as.u8; + return true; + } + + return false; +} + +bool cmp_object_as_ushort(cmp_object_t *obj, uint16_t *s) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + *s = obj->as.u8; + return true; + case CMP_TYPE_UINT16: + *s = obj->as.u16; + return true; + } + + return false; +} + +bool cmp_object_as_uint(cmp_object_t *obj, uint32_t *i) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + *i = obj->as.u8; + return true; + case CMP_TYPE_UINT16: + *i = obj->as.u16; + return true; + case CMP_TYPE_UINT32: + *i = obj->as.u32; + return true; + } + + return false; +} + +bool cmp_object_as_ulong(cmp_object_t *obj, uint64_t *u) { + switch (obj->type) { + case CMP_TYPE_POSITIVE_FIXNUM: + case CMP_TYPE_UINT8: + *u = obj->as.u8; + return true; + case CMP_TYPE_UINT16: + *u = obj->as.u16; + return true; + case CMP_TYPE_UINT32: + *u = obj->as.u32; + return true; + case CMP_TYPE_UINT64: + *u = obj->as.u64; + return true; + } + + return false; +} + +bool cmp_object_as_uinteger(cmp_object_t *obj, uint64_t *d) { + return cmp_object_as_ulong(obj, d); +} + +bool cmp_object_as_float(cmp_object_t *obj, float *f) { + if (obj->type == CMP_TYPE_FLOAT) { + *f = obj->as.flt; + return true; + } + + return false; +} + +bool cmp_object_as_double(cmp_object_t *obj, double *d) { + if (obj->type == CMP_TYPE_DOUBLE) { + *d = obj->as.dbl; + return true; + } + + return false; +} + +bool cmp_object_as_bool(cmp_object_t *obj, bool *b) { + if (obj->type == CMP_TYPE_BOOLEAN) { + if (obj->as.boolean) + *b = true; + else + *b = false; + + return true; + } + + return false; +} + +bool cmp_object_as_str(cmp_object_t *obj, uint32_t *size) { + switch (obj->type) { + case CMP_TYPE_FIXSTR: + case CMP_TYPE_STR8: + case CMP_TYPE_STR16: + case CMP_TYPE_STR32: + *size = obj->as.str_size; + return true; + } + + return false; +} + +bool cmp_object_as_bin(cmp_object_t *obj, uint32_t *size) { + switch (obj->type) { + case CMP_TYPE_BIN8: + case CMP_TYPE_BIN16: + case CMP_TYPE_BIN32: + *size = obj->as.bin_size; + return true; + } + + return false; +} + +bool cmp_object_as_array(cmp_object_t *obj, uint32_t *size) { + switch (obj->type) { + case CMP_TYPE_FIXARRAY: + case CMP_TYPE_ARRAY16: + case CMP_TYPE_ARRAY32: + *size = obj->as.array_size; + return true; + } + + return false; +} + +bool cmp_object_as_map(cmp_object_t *obj, uint32_t *size) { + switch (obj->type) { + case CMP_TYPE_FIXMAP: + case CMP_TYPE_MAP16: + case CMP_TYPE_MAP32: + *size = obj->as.map_size; + return true; + } + + return false; +} + +bool cmp_object_as_ext(cmp_object_t *obj, int8_t *type, uint32_t *size) { + switch (obj->type) { + case CMP_TYPE_FIXEXT1: + case CMP_TYPE_FIXEXT2: + case CMP_TYPE_FIXEXT4: + case CMP_TYPE_FIXEXT8: + case CMP_TYPE_FIXEXT16: + case CMP_TYPE_EXT8: + case CMP_TYPE_EXT16: + case CMP_TYPE_EXT32: + *type = obj->as.ext.type; + *size = obj->as.ext.size; + return true; + } + + return false; +} + +/* vi: set et ts=2 sw=2: */ diff --git a/tracker/cmp.h b/tracker/cmp.h new file mode 100755 index 0000000..fef008e --- /dev/null +++ b/tracker/cmp.h @@ -0,0 +1,404 @@ +#ifndef CMP_H__ +#define CMP_H__ + +struct cmp_ctx_s; + +typedef bool (*cmp_reader)(struct cmp_ctx_s *ctx, void *data, size_t limit); +typedef size_t (*cmp_writer)(struct cmp_ctx_s *ctx, const void *data, + size_t count); + +enum { + CMP_TYPE_POSITIVE_FIXNUM, /* 0 */ + CMP_TYPE_FIXMAP, /* 1 */ + CMP_TYPE_FIXARRAY, /* 2 */ + CMP_TYPE_FIXSTR, /* 3 */ + CMP_TYPE_NIL, /* 4 */ + CMP_TYPE_BOOLEAN, /* 5 */ + CMP_TYPE_BIN8, /* 6 */ + CMP_TYPE_BIN16, /* 7 */ + CMP_TYPE_BIN32, /* 8 */ + CMP_TYPE_EXT8, /* 9 */ + CMP_TYPE_EXT16, /* 10 */ + CMP_TYPE_EXT32, /* 11 */ + CMP_TYPE_FLOAT, /* 12 */ + CMP_TYPE_DOUBLE, /* 13 */ + CMP_TYPE_UINT8, /* 14 */ + CMP_TYPE_UINT16, /* 15 */ + CMP_TYPE_UINT32, /* 16 */ + CMP_TYPE_UINT64, /* 17 */ + CMP_TYPE_SINT8, /* 18 */ + CMP_TYPE_SINT16, /* 19 */ + CMP_TYPE_SINT32, /* 20 */ + CMP_TYPE_SINT64, /* 21 */ + CMP_TYPE_FIXEXT1, /* 22 */ + CMP_TYPE_FIXEXT2, /* 23 */ + CMP_TYPE_FIXEXT4, /* 24 */ + CMP_TYPE_FIXEXT8, /* 25 */ + CMP_TYPE_FIXEXT16, /* 26 */ + CMP_TYPE_STR8, /* 27 */ + CMP_TYPE_STR16, /* 28 */ + CMP_TYPE_STR32, /* 29 */ + CMP_TYPE_ARRAY16, /* 30 */ + CMP_TYPE_ARRAY32, /* 31 */ + CMP_TYPE_MAP16, /* 32 */ + CMP_TYPE_MAP32, /* 33 */ + CMP_TYPE_NEGATIVE_FIXNUM /* 34 */ +}; + +typedef struct cmp_ext_s { + int8_t type; + uint32_t size; +} cmp_ext_t; + +union cmp_object_data_u { + bool boolean; + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + int8_t s8; + int16_t s16; + int32_t s32; + int64_t s64; + float flt; + double dbl; + uint32_t array_size; + uint32_t map_size; + uint32_t str_size; + uint32_t bin_size; + cmp_ext_t ext; +}; + +typedef struct cmp_ctx_s { + uint8_t error; + void *buf; + cmp_reader read; + cmp_writer write; +} cmp_ctx_t; + +typedef struct cmp_object_s { + uint8_t type; + union cmp_object_data_u as; +} cmp_object_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ============================================================================ + * === Main API + * ============================================================================ + */ + +/* Initializes a CMP context */ +void cmp_init(cmp_ctx_t *ctx, void *buf, cmp_reader read, cmp_writer write); + +/* Returns CMP's version */ +uint32_t cmp_version(void); + +/* Returns the MessagePack version employed by CMP */ +uint32_t cmp_mp_version(void); + +/* Returns a string description of a CMP context's error */ +const char* cmp_strerror(cmp_ctx_t *ctx); + +/* Writes a signed integer to the backend */ +bool cmp_write_sint(cmp_ctx_t *ctx, int64_t d); + +/* Writes an unsigned integer to the backend */ +bool cmp_write_uint(cmp_ctx_t *ctx, uint64_t u); + +/* Writes a single-precision float to the backend */ +bool cmp_write_float(cmp_ctx_t *ctx, float f); + +/* Writes a double-precision float to the backend */ +bool cmp_write_double(cmp_ctx_t *ctx, double d); + +/* Writes NULL to the backend */ +bool cmp_write_nil(cmp_ctx_t *ctx); + +/* Writes true to the backend */ +bool cmp_write_true(cmp_ctx_t *ctx); + +/* Writes false to the backend */ +bool cmp_write_false(cmp_ctx_t *ctx); + +/* Writes a boolean value to the backend */ +bool cmp_write_bool(cmp_ctx_t *ctx, bool b); + +/* + * Writes an unsigned char's value to the backend as a boolean. This is useful + * if you are using a different boolean type in your application. + */ +bool cmp_write_u8_as_bool(cmp_ctx_t *ctx, uint8_t b); + +/* + * Writes a string to the backend; according to the MessagePack spec, this must + * be encoded using UTF-8, but CMP leaves that job up to the programmer. + */ +bool cmp_write_str(cmp_ctx_t *ctx, const char *data, uint32_t size); + +/* + * Writes the string marker to the backend. This is useful if you are writing + * data in chunks instead of a single shot. + */ +bool cmp_write_str_marker(cmp_ctx_t *ctx, uint32_t size); + +/* Writes binary data to the backend */ +bool cmp_write_bin(cmp_ctx_t *ctx, const void *data, uint32_t size); + +/* + * Writes the binary data marker to the backend. This is useful if you are + * writing data in chunks instead of a single shot. + */ +bool cmp_write_bin_marker(cmp_ctx_t *ctx, uint32_t size); + +/* Writes an array to the backend. */ +bool cmp_write_array(cmp_ctx_t *ctx, uint32_t size); + +/* Writes a map to the backend. */ +bool cmp_write_map(cmp_ctx_t *ctx, uint32_t size); + +/* Writes an extended type to the backend */ +bool cmp_write_ext(cmp_ctx_t *ctx, int8_t type, uint32_t size, + const void *data); + +/* + * Writes the extended type marker to the backend. This is useful if you want + * to write the type's data in chunks instead of a single shot. + */ +bool cmp_write_ext_marker(cmp_ctx_t *ctx, int8_t type, uint32_t size); + +/* Writes an object to the backend */ +bool cmp_write_object(cmp_ctx_t *ctx, cmp_object_t *obj); + +/* Reads a signed integer that fits inside a signed char */ +bool cmp_read_char(cmp_ctx_t *ctx, int8_t *c); + +/* Reads a signed integer that fits inside a signed short */ +bool cmp_read_short(cmp_ctx_t *ctx, int16_t *s); + +/* Reads a signed integer that fits inside a signed int */ +bool cmp_read_int(cmp_ctx_t *ctx, int32_t *i); + +/* Reads a signed integer that fits inside a signed long */ +bool cmp_read_long(cmp_ctx_t *ctx, int64_t *d); + +/* Reads a signed integer */ +bool cmp_read_sinteger(cmp_ctx_t *ctx, int64_t *d); + +/* Reads an unsigned integer that fits inside an unsigned char */ +bool cmp_read_uchar(cmp_ctx_t *ctx, uint8_t *c); + +/* Reads an unsigned integer that fits inside an unsigned short */ +bool cmp_read_ushort(cmp_ctx_t *ctx, uint16_t *s); + +/* Reads an unsigned integer that fits inside an unsigned int */ +bool cmp_read_uint(cmp_ctx_t *ctx, uint32_t *i); + +/* Reads an unsigned integer that fits inside an unsigned long */ +bool cmp_read_ulong(cmp_ctx_t *ctx, uint64_t *u); + +/* Reads an unsigned integer */ +bool cmp_read_uinteger(cmp_ctx_t *ctx, uint64_t *u); + +/* Reads a single-precision float from the backend */ +bool cmp_read_float(cmp_ctx_t *ctx, float *f); + +/* Reads a double-precision float from the backend */ +bool cmp_read_double(cmp_ctx_t *ctx, double *d); + +/* "Reads" (more like "skips") a NULL value from the backend */ +bool cmp_read_nil(cmp_ctx_t *ctx); + +/* Reads a boolean from the backend */ +bool cmp_read_bool(cmp_ctx_t *ctx, bool *b); + +/* + * Reads a boolean as an unsigned char from the backend; this is useful if your + * application uses a different boolean type. + */ +bool cmp_read_bool_as_u8(cmp_ctx_t *ctx, uint8_t *b); + +/* Reads a string's size from the backend */ +bool cmp_read_str_size(cmp_ctx_t *ctx, uint32_t *size); + +/* + * Reads a string from the backend; according to the spec, the string's data + * ought to be encoded using UTF-8, + */ +bool cmp_read_str(cmp_ctx_t *ctx, char *data, uint32_t *size); + +/* Reads the size of packed binary data from the backend */ +bool cmp_read_bin_size(cmp_ctx_t *ctx, uint32_t *size); + +/* Reads packed binary data from the backend */ +bool cmp_read_bin(cmp_ctx_t *ctx, void *data, uint32_t *size); + +/* Reads an array from the backend */ +bool cmp_read_array(cmp_ctx_t *ctx, uint32_t *size); + +/* Reads a map from the backend */ +bool cmp_read_map(cmp_ctx_t *ctx, uint32_t *size); + +/* Reads the extended type's marker from the backend */ +bool cmp_read_ext_marker(cmp_ctx_t *ctx, int8_t *type, uint32_t *size); + +/* Reads an extended type from the backend */ +bool cmp_read_ext(cmp_ctx_t *ctx, int8_t *type, uint32_t *size, void *data); + +/* Reads an object from the backend */ +bool cmp_read_object(cmp_ctx_t *ctx, cmp_object_t *obj); + +/* + * ============================================================================ + * === Specific API + * ============================================================================ + */ + +bool cmp_write_pfix(cmp_ctx_t *ctx, uint8_t c); +bool cmp_write_nfix(cmp_ctx_t *ctx, int8_t c); + +bool cmp_write_sfix(cmp_ctx_t *ctx, int8_t c); +bool cmp_write_s8(cmp_ctx_t *ctx, int8_t c); +bool cmp_write_s16(cmp_ctx_t *ctx, int16_t s); +bool cmp_write_s32(cmp_ctx_t *ctx, int32_t i); +bool cmp_write_s64(cmp_ctx_t *ctx, int64_t l); + +bool cmp_write_ufix(cmp_ctx_t *ctx, uint8_t c); +bool cmp_write_u8(cmp_ctx_t *ctx, uint8_t c); +bool cmp_write_u16(cmp_ctx_t *ctx, uint16_t s); +bool cmp_write_u32(cmp_ctx_t *ctx, uint32_t i); +bool cmp_write_u64(cmp_ctx_t *ctx, uint64_t l); + +bool cmp_write_fixstr_marker(cmp_ctx_t *ctx, uint8_t size); +bool cmp_write_fixstr(cmp_ctx_t *ctx, const char *data, uint8_t size); +bool cmp_write_str8_marker(cmp_ctx_t *ctx, uint8_t size); +bool cmp_write_str8(cmp_ctx_t *ctx, const char *data, uint8_t size); +bool cmp_write_str16_marker(cmp_ctx_t *ctx, uint16_t size); +bool cmp_write_str16(cmp_ctx_t *ctx, const char *data, uint16_t size); +bool cmp_write_str32_marker(cmp_ctx_t *ctx, uint32_t size); +bool cmp_write_str32(cmp_ctx_t *ctx, const char *data, uint32_t size); + +bool cmp_write_bin8_marker(cmp_ctx_t *ctx, uint8_t size); +bool cmp_write_bin8(cmp_ctx_t *ctx, const void *data, uint8_t size); +bool cmp_write_bin16_marker(cmp_ctx_t *ctx, uint16_t size); +bool cmp_write_bin16(cmp_ctx_t *ctx, const void *data, uint16_t size); +bool cmp_write_bin32_marker(cmp_ctx_t *ctx, uint32_t size); +bool cmp_write_bin32(cmp_ctx_t *ctx, const void *data, uint32_t size); + +bool cmp_write_fixarray(cmp_ctx_t *ctx, uint8_t size); +bool cmp_write_array16(cmp_ctx_t *ctx, uint16_t size); +bool cmp_write_array32(cmp_ctx_t *ctx, uint32_t size); + +bool cmp_write_fixmap(cmp_ctx_t *ctx, uint8_t size); +bool cmp_write_map16(cmp_ctx_t *ctx, uint16_t size); +bool cmp_write_map32(cmp_ctx_t *ctx, uint32_t size); + +bool cmp_write_fixext1_marker(cmp_ctx_t *ctx, int8_t type); +bool cmp_write_fixext1(cmp_ctx_t *ctx, int8_t type, const void *data); +bool cmp_write_fixext2_marker(cmp_ctx_t *ctx, int8_t type); +bool cmp_write_fixext2(cmp_ctx_t *ctx, int8_t type, const void *data); +bool cmp_write_fixext4_marker(cmp_ctx_t *ctx, int8_t type); +bool cmp_write_fixext4(cmp_ctx_t *ctx, int8_t type, const void *data); +bool cmp_write_fixext8_marker(cmp_ctx_t *ctx, int8_t type); +bool cmp_write_fixext8(cmp_ctx_t *ctx, int8_t type, const void *data); +bool cmp_write_fixext16_marker(cmp_ctx_t *ctx, int8_t type); +bool cmp_write_fixext16(cmp_ctx_t *ctx, int8_t type, const void *data); + +bool cmp_write_ext8_marker(cmp_ctx_t *ctx, int8_t type, uint8_t size); +bool cmp_write_ext8(cmp_ctx_t *ctx, int8_t type, uint8_t size, + const void *data); +bool cmp_write_ext16_marker(cmp_ctx_t *ctx, int8_t type, uint16_t size); +bool cmp_write_ext16(cmp_ctx_t *ctx, int8_t type, uint16_t size, + const void *data); +bool cmp_write_ext32_marker(cmp_ctx_t *ctx, int8_t type, uint32_t size); +bool cmp_write_ext32(cmp_ctx_t *ctx, int8_t type, uint32_t size, + const void *data); + +bool cmp_read_pfix(cmp_ctx_t *ctx, uint8_t *c); +bool cmp_read_nfix(cmp_ctx_t *ctx, int8_t *c); + +bool cmp_read_sfix(cmp_ctx_t *ctx, int8_t *c); +bool cmp_read_s8(cmp_ctx_t *ctx, int8_t *c); +bool cmp_read_s16(cmp_ctx_t *ctx, int16_t *s); +bool cmp_read_s32(cmp_ctx_t *ctx, int32_t *i); +bool cmp_read_s64(cmp_ctx_t *ctx, int64_t *l); + +bool cmp_read_ufix(cmp_ctx_t *ctx, uint8_t *c); +bool cmp_read_u8(cmp_ctx_t *ctx, uint8_t *c); +bool cmp_read_u16(cmp_ctx_t *ctx, uint16_t *s); +bool cmp_read_u32(cmp_ctx_t *ctx, uint32_t *i); +bool cmp_read_u64(cmp_ctx_t *ctx, uint64_t *l); + +bool cmp_read_fixext1_marker(cmp_ctx_t *ctx, int8_t *type); +bool cmp_read_fixext1(cmp_ctx_t *ctx, int8_t *type, void *data); +bool cmp_read_fixext2_marker(cmp_ctx_t *ctx, int8_t *type); +bool cmp_read_fixext2(cmp_ctx_t *ctx, int8_t *type, void *data); +bool cmp_read_fixext4_marker(cmp_ctx_t *ctx, int8_t *type); +bool cmp_read_fixext4(cmp_ctx_t *ctx, int8_t *type, void *data); +bool cmp_read_fixext8_marker(cmp_ctx_t *ctx, int8_t *type); +bool cmp_read_fixext8(cmp_ctx_t *ctx, int8_t *type, void *data); +bool cmp_read_fixext16_marker(cmp_ctx_t *ctx, int8_t *type); +bool cmp_read_fixext16(cmp_ctx_t *ctx, int8_t *type, void *data); + +bool cmp_read_ext8_marker(cmp_ctx_t *ctx, int8_t *type, uint8_t *size); +bool cmp_read_ext8(cmp_ctx_t *ctx, int8_t *type, uint8_t *size, void *data); +bool cmp_read_ext16_marker(cmp_ctx_t *ctx, int8_t *type, uint16_t *size); +bool cmp_read_ext16(cmp_ctx_t *ctx, int8_t *type, uint16_t *size, void *data); +bool cmp_read_ext32_marker(cmp_ctx_t *ctx, int8_t *type, uint32_t *size); +bool cmp_read_ext32(cmp_ctx_t *ctx, int8_t *type, uint32_t *size, void *data); + +/* + * ============================================================================ + * === Object API + * ============================================================================ + */ + +bool cmp_object_is_char(cmp_object_t *obj); +bool cmp_object_is_short(cmp_object_t *obj); +bool cmp_object_is_int(cmp_object_t *obj); +bool cmp_object_is_long(cmp_object_t *obj); +bool cmp_object_is_sinteger(cmp_object_t *obj); +bool cmp_object_is_uchar(cmp_object_t *obj); +bool cmp_object_is_ushort(cmp_object_t *obj); +bool cmp_object_is_uint(cmp_object_t *obj); +bool cmp_object_is_ulong(cmp_object_t *obj); +bool cmp_object_is_uinteger(cmp_object_t *obj); +bool cmp_object_is_float(cmp_object_t *obj); +bool cmp_object_is_double(cmp_object_t *obj); +bool cmp_object_is_nil(cmp_object_t *obj); +bool cmp_object_is_bool(cmp_object_t *obj); +bool cmp_object_is_str(cmp_object_t *obj); +bool cmp_object_is_bin(cmp_object_t *obj); +bool cmp_object_is_array(cmp_object_t *obj); +bool cmp_object_is_map(cmp_object_t *obj); +bool cmp_object_is_ext(cmp_object_t *obj); + +bool cmp_object_as_char(cmp_object_t *obj, int8_t *c); +bool cmp_object_as_short(cmp_object_t *obj, int16_t *s); +bool cmp_object_as_int(cmp_object_t *obj, int32_t *i); +bool cmp_object_as_long(cmp_object_t *obj, int64_t *d); +bool cmp_object_as_sinteger(cmp_object_t *obj, int64_t *d); +bool cmp_object_as_uchar(cmp_object_t *obj, uint8_t *c); +bool cmp_object_as_ushort(cmp_object_t *obj, uint16_t *s); +bool cmp_object_as_uint(cmp_object_t *obj, uint32_t *i); +bool cmp_object_as_ulong(cmp_object_t *obj, uint64_t *u); +bool cmp_object_as_uinteger(cmp_object_t *obj, uint64_t *u); +bool cmp_object_as_float(cmp_object_t *obj, float *f); +bool cmp_object_as_double(cmp_object_t *obj, double *d); +bool cmp_object_as_bool(cmp_object_t *obj, bool *b); +bool cmp_object_as_str(cmp_object_t *obj, uint32_t *size); +bool cmp_object_as_bin(cmp_object_t *obj, uint32_t *size); +bool cmp_object_as_array(cmp_object_t *obj, uint32_t *size); +bool cmp_object_as_map(cmp_object_t *obj, uint32_t *size); +bool cmp_object_as_ext(cmp_object_t *obj, int8_t *type, uint32_t *size); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* CMP_H__ */ + +/* vi: set et ts=2 sw=2: */ diff --git a/tracker/cutdown.c b/tracker/cutdown.c new file mode 100755 index 0000000..998a122 --- /dev/null +++ b/tracker/cutdown.c @@ -0,0 +1,153 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gps.h" +#include "misc.h" +#include "cutdown.h" + +int CutdownPeriod = 0; + +void cutdown_checks(struct TGPS *GPS) +{ + if (Config.EnableCutdown) + { + if (GPS->CutdownStatus == csUnarmed) + { + if (GPS->Altitude >= Config.MinCutdownAltitude) + { + GPS->CutdownStatus = csArmed; + } + } + + if (GPS->CutdownStatus == csArmed) + { + if ((Config.CutdownAltitude > 0) && (GPS->Altitude >= Config.CutdownAltitude)) + { + GPS->CutdownStatus = csAltitude; + printf("Cutdown (Altitude) Triggered\n"); + } + + if ((Config.MinCutdownAltitude > 0) && (GPS->Altitude >= Config.MinCutdownAltitude)) + { + // Other tests enabled now + if (strcasecmp("N", Config.CutdownTest) == 0) + { + if (GPS->Latitude >= Config.CutdownLatitude) + { + GPS->CutdownStatus = csPosition; + } + } + else if (strcasecmp("NE", Config.CutdownTest) == 0) + { + if ((GPS->Latitude >= Config.CutdownLatitude) && (GPS->Longitude >= Config.CutdownLongitude)) + { + GPS->CutdownStatus = csPosition; + } + } + else if (strcasecmp("E", Config.CutdownTest) == 0) + { + if (GPS->Longitude >= Config.CutdownLongitude) + { + GPS->CutdownStatus = csPosition; + } + } + else if (strcasecmp("SE", Config.CutdownTest) == 0) + { + if ((GPS->Latitude <= Config.CutdownLatitude) && (GPS->Longitude >= Config.CutdownLongitude)) + { + GPS->CutdownStatus = csPosition; + } + } + else if (strcasecmp("S", Config.CutdownTest) == 0) + { + if (GPS->Latitude <= Config.CutdownLatitude) + { + GPS->CutdownStatus = csPosition; + } + } + else if (strcasecmp("SW", Config.CutdownTest) == 0) + { + if ((GPS->Latitude <= Config.CutdownLatitude) && (GPS->Longitude <= Config.CutdownLongitude)) + { + GPS->CutdownStatus = csPosition; + } + } + else if (strcasecmp("W", Config.CutdownTest) == 0) + { + if (GPS->Longitude <= Config.CutdownLongitude) + { + GPS->CutdownStatus = csPosition; + } + } + else if (strcasecmp("NW", Config.CutdownTest) == 0) + { + if ((GPS->Latitude >= Config.CutdownLatitude) && (GPS->Longitude <= Config.CutdownLongitude)) + { + GPS->CutdownStatus = csPosition; + } + } + if (GPS->CutdownStatus == csPosition) + + { + printf("Cutdown (Position) Triggered\n"); + } + + if ((Config.CutdownTimeSinceLaunch > 0) && (GPS->SecondsSinceLaunch >= Config.CutdownTimeSinceLaunch)) + { + GPS->CutdownStatus = csFlightTime; + printf("Cutdown (Max Flight Time) Triggered\n"); + } + + if (Config.CutdownBurst && (GPS->FlightMode >= fmDescending) && (GPS->FlightMode < fmLanding)) + { + GPS->CutdownStatus = csBurst; + printf("Cutdown (Balloon burst detected\n"); + } + } + + if (GPS->CutdownStatus > csArmed) + { + Cutdown(Config.CutdownPeriod); + } + } + } +} + +void Cutdown(int Period) +{ + CutdownPeriod = Period; +} + +void *CutdownLoop(void *some_void_ptr) +{ + if (Config.CutdownPin > 0) + { + // Configure output + pinMode (Config.CutdownPin, OUTPUT); + digitalWrite (Config.CutdownPin, 0); + + while (1) + { + if (CutdownPeriod > 0) + { + digitalWrite (Config.CutdownPin, 1); + sleep(CutdownPeriod); + digitalWrite (Config.CutdownPin, 0); + CutdownPeriod = 0; + } + + sleep(1); + } + } + + return 0; +} diff --git a/tracker/cutdown.h b/tracker/cutdown.h new file mode 100755 index 0000000..f6dae04 --- /dev/null +++ b/tracker/cutdown.h @@ -0,0 +1,4 @@ +void cutdown_checks(struct TGPS *GPS); +void Cutdown(int Period); +void *CutdownLoop(void *some_void_ptr); + diff --git a/tracker/gopro.py b/tracker/gopro.py new file mode 100755 index 0000000..b62163d --- /dev/null +++ b/tracker/gopro.py @@ -0,0 +1,54 @@ +from goprocam import GoProCamera +import os +import time +from jpegtran import JPEGImage + +camera = GoProCamera.GoPro() + +if not os.path.isdir('images'): + os.mkdir('images') +for i in range(0,5): + folder = 'images/' + ['RTTY','APRS','LORA0','LORA1','FULL'][i] + if not os.path.isdir(folder): + os.mkdir(folder) + +while True: + for i in range(0,5): + filename = 'take_pic_' + str(i) + if os.path.isfile(filename): + os.remove(filename) + os.chdir('images/' + ['RTTY','APRS','LORA0','LORA1','FULL'][i]) + camera.downloadLastMedia(camera.take_photo(0)) + os.chdir('../..') + + filename = 'convert_' + str(i) + if os.path.isfile(filename): + # Read and delete conversion file + file = open(filename, 'r') + lines = file.read().splitlines() + file.close() + + if os.path.isfile('ssdv.jpg'): + os.remove('ssdv.jpg') + + # resize selected image file + image=JPEGImage(lines[3]) + image.downscale(int(lines[4]), int(lines[5])).save('ssdv.jpg') + + # run SSDV conversion + os.system('ssdv ' + lines[1] + ' -e -c ' + lines[0] + ' -i ' + lines[2] + ' ssdv.jpg ' + lines[6]) + + # Move all images that werre considered + folder1 = "images/" + ['RTTY','APRS','LORA0','LORA1','FULL'][i] + folder2 = folder1 + "/" + time.strftime("%d_%m_%y") + if not os.path.isdir(folder2): + os.mkdir(folder2) + try: + os.system("mv " + folder1 + "/*.JPG " + folder2) + os.remove(filename) + finally: + pass + os.system("echo DONE > ssdv_done_" + str(i)) + + time.sleep(1) +done diff --git a/tracker/gps.c b/tracker/gps.c index c0a90a6..6c1146a 100755 --- a/tracker/gps.c +++ b/tracker/gps.c @@ -13,6 +13,7 @@ #define _GNU_SOURCE 1 + #include #include #include @@ -29,6 +30,7 @@ #include #include "gps.h" #include "misc.h" +#include "cutdown.h" struct gps_info { @@ -374,16 +376,19 @@ uint8_t GPSGetc(struct gps_info *bb) int GPSChecksumOK(char *Buffer, int Count) { - unsigned char XOR, i, c; + unsigned char XOR, i, c, Star; - XOR = 0; - for (i = 1; i < (Count-4); i++) - { - c = Buffer[i]; - XOR ^= c; - } + // Find star + for (Star=Count; (Star > 1) && (Buffer[Star] != '*'); Star--); + + XOR = 0; + for (i = 1; i < Star; i++) + { + c = Buffer[i]; + XOR ^= c; + } - return (Buffer[Count-4] == '*') && (Buffer[Count-3] == Hex(XOR >> 4)) && (Buffer[Count-2] == Hex(XOR & 15)); + return (Buffer[Star] == '*') && (Buffer[Star+1] == Hex(XOR >> 4)) && (Buffer[Star+2] == Hex(XOR & 15)); } void FixUBXChecksum(unsigned char *Message, int Length) @@ -405,7 +410,7 @@ void FixUBXChecksum(unsigned char *Message, int Length) } -void SendUBX(struct gps_info *bb, unsigned char *MSG, int len) +void SendToGPS(struct gps_info *bb, unsigned char *MSG, int len) { if (bb->ConnectionMode == cmSerial) { @@ -417,58 +422,97 @@ void SendUBX(struct gps_info *bb, unsigned char *MSG, int len) } } -void SetFlightMode(struct gps_info *bb) +void SetFlightMode(struct gps_info *bb, int High) { - // Send navigation configuration command - unsigned char setNav[] = {0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x05, 0x00, 0xFA, 0x00, 0xFA, 0x00, 0x64, 0x00, 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xDC}; - SendUBX(bb, setNav, sizeof(setNav)); - printf ("Setting flight mode\n"); + if (Config.GPSModel == 'U') + { + // Send navigation configuration command + unsigned char FlightMode[] = {0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x05, 0x00, 0xFA, 0x00, 0xFA, 0x00, 0x64, 0x00, 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xDC}; + unsigned char PedestrianMode[] = {0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x05, 0x00, 0xFA, 0x00, 0xFA, 0x00, 0x64, 0x00, 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x76}; + + if (High) + { + SendToGPS(bb, FlightMode, sizeof(FlightMode)); + printf ("Setting UBlox flight mode\n"); + } + else + { + SendToGPS(bb, PedestrianMode, sizeof(PedestrianMode)); + printf ("Setting UBlox pedestrian mode\n"); + } + } + else if (Config.GPSModel == 'M') + { + // char *VehicleMode = "$PMTK886,0*28\r\n"; + char *PedestrianMode = "$PMTK886,1*29\r\n"; + char *BalloonMode = "$PMTK886,3*2B\r\n"; + + if (High) + { + SendToGPS(bb, (unsigned char *)BalloonMode, strlen(BalloonMode)); + printf ("Setting L80 balloon mode\n"); + } + else + { + SendToGPS(bb, (unsigned char *)PedestrianMode, strlen(PedestrianMode)); + printf ("Setting L80 pedestrian mode\n"); + } + } } void SetPowerMode(struct gps_info *bb, int SavePower) { - unsigned char setPSM[] = {0xB5, 0x62, 0x06, 0x11, 0x02, 0x00, 0x08, 0x01, 0x22, 0x92 }; - - setPSM[7] = SavePower ? 1 : 0; - - printf ("Setting power-saving %s\n", SavePower ? "ON" : "OFF"); - - FixUBXChecksum(setPSM, sizeof(setPSM)); - - SendUBX(bb, setPSM, sizeof(setPSM)); + if (Config.GPSModel == 'U') + { + unsigned char setPSM[] = {0xB5, 0x62, 0x06, 0x11, 0x02, 0x00, 0x08, 0x01, 0x22, 0x92 }; + + setPSM[7] = SavePower ? 1 : 0; + + printf ("Setting power-saving %s\n", SavePower ? "ON" : "OFF"); + + FixUBXChecksum(setPSM, sizeof(setPSM)); + + SendToGPS(bb, setPSM, sizeof(setPSM)); + } } void setGPS_GNSS(struct gps_info *bb) { - // Sets CFG-GNSS to disable everything other than GPS GNSS - // solution. Failure to do this means GPS power saving - // doesn't work. Not needed for MAX7, needed for MAX8's - unsigned char setgnss[] = { - 0xB5, 0x62, 0x06, 0x3E, 0x2C, 0x00, 0x00, 0x00, - 0x20, 0x05, 0x00, 0x08, 0x10, 0x00, 0x01, 0x00, - 0x01, 0x01, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x03, 0x08, 0x10, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x05, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x06, 0x08, 0x0E, 0x00, 0x00, 0x00, - 0x01, 0x01, 0xFC, 0x11 }; - - printf ("Disabling GNSS\n"); - - SendUBX(bb, setgnss, sizeof(setgnss)); + if (Config.GPSModel == 'U') + { + // Sets CFG-GNSS to disable everything other than GPS GNSS + // solution. Failure to do this means GPS power saving + // doesn't work. Not needed for MAX7, needed for MAX8's + unsigned char setgnss[] = { + 0xB5, 0x62, 0x06, 0x3E, 0x2C, 0x00, 0x00, 0x00, + 0x20, 0x05, 0x00, 0x08, 0x10, 0x00, 0x01, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x03, 0x08, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x06, 0x08, 0x0E, 0x00, 0x00, 0x00, + 0x01, 0x01, 0xFC, 0x11 }; + + printf ("Disabling GNSS\n"); + + SendToGPS(bb, setgnss, sizeof(setgnss)); + } } void setGPS_DynamicModel6(struct gps_info *bb) { - uint8_t setdm6[] = { - 0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x06, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, - 0x05, 0x00, 0xFA, 0x00, 0xFA, 0x00, 0x64, 0x00, 0x2C, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xDC }; + if (Config.GPSModel == 'U') + { + uint8_t setdm6[] = { + 0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x06, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, + 0x05, 0x00, 0xFA, 0x00, 0xFA, 0x00, 0x64, 0x00, 0x2C, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xDC}; - printf ("Setting dynamic model 6\n"); + printf ("Setting dynamic model 6\n"); - SendUBX(bb, setdm6, sizeof(setdm6)); + SendToGPS(bb, setdm6, sizeof(setdm6)); + } } float FixPosition(float Position) @@ -496,20 +540,23 @@ time_t day_seconds() return t1 - t2; } -void ProcessLine(struct gps_info *bb, struct TGPS *GPS, char *Buffer, int Count, int ActionMask) +int ProcessLine(struct gps_info *bb, struct TGPS *GPS, char *Buffer, int Count, int ActionMask) { static int SystemTimeHasBeenSet=0; float utc_time, latitude, longitude, hdop, altitude; - int lock, satellites; + int lock, satellites, result; char active, ns, ew, units, timestring[16], speedstring[16], *course, *date, restofline[80], *ptr; + result = 0; + if (GPSChecksumOK(Buffer, Count)) { satellites = 0; if (strncmp(Buffer+3, "GGA", 3) == 0) { + result = 1; GPS->MessageCount++; if (GPS->FlightMode >= fmLaunched) { @@ -541,12 +588,13 @@ void ProcessLine(struct gps_info *bb, struct TGPS *GPS, char *Buffer, int Count, # ifdef EXTRAS_PRESENT gps_postprocess_position(GPS, ActionMask, latitude, longitude); - cutdown_checks(GPS); # else GPS->Latitude = latitude; GPS->Longitude = longitude; # endif - + + cutdown_checks(GPS); + if (GPS->Altitude <= 0) { GPS->AscentRate = 0; @@ -560,6 +608,8 @@ void ProcessLine(struct gps_info *bb, struct TGPS *GPS, char *Buffer, int Count, if (GPS->Altitude > GPS->MaximumAltitude) { GPS->MaximumAltitude = GPS->Altitude; + GPS->BurstLatitude = GPS->Latitude; + GPS->BurstLongitude = GPS->Longitude; } if ((GPS->Altitude < GPS->MinimumAltitude) || (GPS->MinimumAltitude == 0)) { @@ -590,6 +640,28 @@ void ProcessLine(struct gps_info *bb, struct TGPS *GPS, char *Buffer, int Count, GPS->FlightMode = fmLanded; printf("*** LANDED ***\n"); } + + // Strobe control + if (Config.BlinkenLight >= 0) + { + if ((GPS->FlightMode >= fmDescending) && (GPS->Altitude < Config.FlashBelow)) + { + if (!Config.Flashing) + { + ControlPWMOutput(Config.BlinkenLight, 2000); + Config.Flashing = 1; + } + } + } + + // Strobe control + if (Config.PiezoPin >= 0) + { + if ((GPS->FlightMode >= fmDescending) && (GPS->Altitude < Config.WhistleBelow)) + { + digitalWrite(Config.PiezoPin, 1); + } + } } } if (ActionMask & 2) @@ -597,10 +669,8 @@ void ProcessLine(struct gps_info *bb, struct TGPS *GPS, char *Buffer, int Count, GPS->Satellites = satellites; } } - if (Config.EnableGPSLogging) - { - WriteLog("gps.txt", Buffer); - } + + WriteGPSLog(Buffer); } else if (strncmp(Buffer+3, "RMC", 3) == 0) { @@ -622,7 +692,8 @@ void ProcessLine(struct gps_info *bb, struct TGPS *GPS, char *Buffer, int Count, { struct tm tm; char timedatestring[32]; - time_t t; + + struct timespec tp; // Now create a tm structure from our date and time memset(&tm, 0, sizeof(struct tm)); @@ -630,62 +701,76 @@ void ProcessLine(struct gps_info *bb, struct TGPS *GPS, char *Buffer, int Count, date[0], date[1], date[2], date[3], date[4], date[5], timestring[0], timestring[1], timestring[2], timestring[3], timestring[4], timestring[5]); strptime(timedatestring, "%d-%m-%Y %H:%M:%S", &tm); - - t = mktime(&tm); - if (stime(&t) == -1) + + tp.tv_sec = mktime(&tm); + tp.tv_nsec = 0; + if (clock_settime(CLOCK_REALTIME, &tp) == 0) { - printf("Failed to set system time\n"); + printf("System time set from GPS time\n"); + SystemTimeHasBeenSet = 1; } else { - printf("System time set from GPS time\n"); - SystemTimeHasBeenSet = 1; + printf("Failed to set system time\n"); } } } - if (Config.EnableGPSLogging) - { - WriteLog("gps.txt", Buffer); - } + WriteGPSLog(Buffer); } else if (strncmp(Buffer+3, "GSV", 3) == 0) { // Disable GSV - printf("Disabling GSV\r\n"); - unsigned char setGSV[] = { 0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x39 }; - SendUBX(bb, setGSV, sizeof(setGSV)); + if (Config.GPSModel == 'U') + { + printf("Disabling GSV\r\n"); + unsigned char setGSV[] = { 0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x39 }; + SendToGPS(bb, setGSV, sizeof(setGSV)); + } + else if (Config.GPSModel == 'M') + { + printf("Disabling GSV etc\r\n"); + char *setRMCGGAOnly = "$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28\r\n"; + SendToGPS(bb, (unsigned char *)setRMCGGAOnly, strlen(setRMCGGAOnly)); + } } else if (strncmp(Buffer+3, "GLL", 3) == 0) { // Disable GLL - printf("Disabling GLL\r\n"); - unsigned char setGLL[] = { 0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x2B }; - SendUBX(bb, setGLL, sizeof(setGLL)); + if (Config.GPSModel == 'U') + { + printf("Disabling GLL\r\n"); + unsigned char setGLL[] = { 0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x2B }; + SendToGPS(bb, setGLL, sizeof(setGLL)); + } } else if (strncmp(Buffer+3, "GSA", 3) == 0) { // Disable GSA - printf("Disabling GSA\r\n"); - unsigned char setGSA[] = { 0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x32 }; - SendUBX(bb, setGSA, sizeof(setGSA)); + if (Config.GPSModel == 'U') + { + printf("Disabling GSA\r\n"); + unsigned char setGSA[] = { 0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x32 }; + SendToGPS(bb, setGSA, sizeof(setGSA)); + } } else if (strncmp(Buffer+3, "VTG", 3) == 0) { // Disable VTG - printf("Disabling VTG\r\n"); - unsigned char setVTG[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x05, 0x47}; - SendUBX(bb, setVTG, sizeof(setVTG)); - } - else - { - printf("Unknown NMEA sentence: %s\n", Buffer); + if (Config.GPSModel == 'U') + { + printf("Disabling VTG\r\n"); + unsigned char setVTG[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x05, 0x47}; + SendToGPS(bb, setVTG, sizeof(setVTG)); + } } } else { - printf("Bad checksum\r\n"); + printf("Bad checksum %d %d %s\r\n", Count, strlen(Buffer), Buffer); } + + return result; } @@ -756,27 +841,54 @@ void *GPSLoop(void *some_void_ptr) if (Character == '\n') { Line[Length] = '\0'; - // puts(Line); - if ((fp != NULL) && (strstr(Line, "GGA") != NULL)) + // if ((fp != NULL) && (strstr(Line, "GGA") != NULL)) + if (fp != NULL) { + // Get time only from GPS, then get position from NMEA file static int PostFileCount=0; static char Buffer[100]; - - // Get time only from GPS, then get position from NMEA file - if (fgets(Buffer, sizeof Buffer, fp) == NULL) + + if (ProcessLine(&bb, GPS, Line, Length, 1)) { - if (++PostFileCount > 20) + int Done=0; + + if (Config.ShowGPS) { - fclose(fp); - exit(1); + printf("REAL: %s", Line); + } + + while (!Done) + { + if (fgets(Buffer, sizeof Buffer, fp) == NULL) + { + Done = 1; + if (++PostFileCount > 20) + { + fclose(fp); + exit(1); + } + } + + if (ProcessLine(&bb, GPS, Buffer, strlen(Buffer), 2)) + { + Done = 1; + + if (Config.ShowGPS) + { + printf("FILE: %s", Buffer); + } + } } } - ProcessLine(NULL, GPS, Line, Length, 1); - ProcessLine(NULL, GPS, Buffer, strlen(Buffer)-1, 2); } else { + if (Config.ShowGPS) + { + printf("%s", Line); + } + ProcessLine(&bb, GPS, Line, Length, 3); if (++SentenceCount > 100) SentenceCount = 0; @@ -795,7 +907,7 @@ void *GPSLoop(void *some_void_ptr) } else if (SentenceCount == 40) { - SetFlightMode(&bb); + SetFlightMode(&bb, GPS->Altitude > Config.Flight_Mode_Altitude); } } diff --git a/tracker/gps.h b/tracker/gps.h index 880b8bc..8a01cc9 100755 --- a/tracker/gps.h +++ b/tracker/gps.h @@ -1,13 +1,14 @@ // Types -typedef enum {fmIdle, fmLaunched, fmDescending, fmHoming, fmDirect, fmDownwind, fmLanding, fmLanded} TFlightMode; +typedef enum {fmIdle, fmLaunched, fmDescending, fmHoming, fmDirect, fmDownwind, fmUpwind, fmLanding, fmLanded} TFlightMode; +typedef enum {csUnarmed, csArmed, csAltitude, csPosition, csFlightTime, csBurst, csManual} TCutdownStatus; struct TGPS { // GPS long SecondsInDay; // Time in seconds since midnight int Hours, Minutes, Seconds; - float Longitude, Latitude; + double Longitude, Latitude; int32_t Altitude; unsigned int Satellites; int Speed; @@ -16,6 +17,7 @@ struct TGPS // Calculated from GPS int32_t MaximumAltitude, MinimumAltitude; + double BurstLatitude, BurstLongitude; float AscentRate; // Sensors @@ -31,7 +33,7 @@ struct TGPS TFlightMode FlightMode; // Prediction - float PredictedLongitude, PredictedLatitude; + double PredictedLongitude, PredictedLatitude; float PredictedLandingSpeed; int TimeTillLanding; float CDA; @@ -40,6 +42,10 @@ struct TGPS // int PowerMode; // int Lock; unsigned int MessageCount; + + // Cutdown + TCutdownStatus CutdownStatus; + # ifdef EXTRAS_PRESENT # include "ex_gps.h" @@ -55,7 +61,5 @@ void *GPSLoop(void *some_void_ptr); #ifdef EXTRAS_PRESENT void gps_postprocess_position(struct TGPS *GPS, int ActionMask, float latitude, float longitude); void gps_flight_modes(struct TGPS *GPS); - -void cutdown_checks(struct TGPS *GPS); #endif diff --git a/tracker/habpack.c b/tracker/habpack.c new file mode 100755 index 0000000..991b975 --- /dev/null +++ b/tracker/habpack.c @@ -0,0 +1,116 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gps.h" +#include "misc.h" +#include "cmp.h" +#include "habpack.h" + +int HABpackBufferLength; + +static size_t file_writer(cmp_ctx_t *ctx, const void *data, size_t count) { + + uint16_t i; + + for (i = 0; i < count; i++) + { + ((char*)ctx->buf)[HABpackBufferLength] = *((uint8_t*)data); + data++; + HABpackBufferLength++; + } + return count; +} + +int BuildHABpackPacket(unsigned char *Packet, int Channel, struct TGPS *GPS) +{ + int32_t _latitude = GPS->Latitude * 10e6; + int32_t _longitude = GPS->Longitude * 10e6; + int32_t _altitude = GPS->Altitude; + uint8_t _hour = GPS->Hours; + uint8_t _minute = GPS->Minutes; + uint8_t _second = GPS->Seconds; + uint8_t _sats = GPS->Satellites; + uint8_t total_send, send_voltage, send_prediction, send_temperature; + + Config.Channels[Channel].SentenceCounter++; + + cmp_ctx_t cmp; + cmp_init(&cmp, (void*)Packet, 0, file_writer); + HABpackBufferLength = 0; + + send_voltage = ((Config.BoardType != 3) && (Config.BoardType != 4) && (!Config.DisableADC)) ? 1 : 0; + send_prediction = (Config.EnableLandingPrediction && (Config.PredictionID[0] == '\0')) ? 1 : 0; + send_temperature = (GPS->DS18B20Count > 1) ? 2 : 1; + + total_send = 5 + send_temperature + send_voltage + send_prediction; + + cmp_write_map(&cmp,total_send); + + cmp_write_uint(&cmp, HABPACK_CALLSIGN); + cmp_write_str(&cmp, Config.Channels[Channel].PayloadID, strlen(Config.Channels[Channel].PayloadID)); + + cmp_write_uint(&cmp, HABPACK_SENTENCE_ID); + cmp_write_uint(&cmp, Config.Channels[Channel].SentenceCounter); + + cmp_write_uint(&cmp, HABPACK_TIME); + cmp_write_uint(&cmp, (uint32_t)_hour*(3600) + (uint32_t)_minute*60 + (uint32_t)_second); + + cmp_write_uint(&cmp, HABPACK_POSITION); + cmp_write_array(&cmp, 3); // 3 fields to follow - lat/lon/alt + cmp_write_sint(&cmp, _latitude); + cmp_write_sint(&cmp, _longitude); + cmp_write_sint(&cmp, _altitude); + + cmp_write_uint(&cmp, HABPACK_GNSS_SATELLITES); + cmp_write_uint(&cmp, _sats); + + cmp_write_uint(&cmp, HABPACK_INTERNAL_TEMPERATURE); + cmp_write_sint(&cmp, (int32_t)(GPS->DS18B20Temperature[(GPS->DS18B20Count > 1) ? (1-Config.ExternalDS18B20) : 0] * 1000.0)); + if (send_temperature > 1) + { + cmp_write_uint(&cmp, HABPACK_EXTERNAL_TEMPERATURE); + cmp_write_sint(&cmp, (int32_t)(GPS->DS18B20Temperature[Config.ExternalDS18B20] * 1000.0)); + } + + if (send_voltage) + { + cmp_write_uint(&cmp, HABPACK_VOLTAGE); + cmp_write_sint(&cmp, (int32_t)(GPS->BatteryVoltage * 1000)); + } + + // Landing Prediction, if enabled + if (send_prediction) + { + cmp_write_uint(&cmp, HABPACK_PREDICTED_LANDING_POSITION); + cmp_write_array(&cmp, 2); // 2 fields to follow, lat/lon + + cmp_write_sint(&cmp, GPS->PredictedLatitude * 10e6); + cmp_write_sint(&cmp, GPS->PredictedLongitude * 10e6); + } + + return HABpackBufferLength; +} + diff --git a/tracker/habpack.h b/tracker/habpack.h new file mode 100755 index 0000000..30a4535 --- /dev/null +++ b/tracker/habpack.h @@ -0,0 +1,47 @@ +#ifndef __HABPACK_H__ +#define __HABPACK_H__ + +/* Core Telemetry */ +#define HABPACK_CALLSIGN 0 +#define HABPACK_SENTENCE_ID 1 +#define HABPACK_TIME 2 +#define HABPACK_POSITION 3 +#define HABPACK_GNSS_SATELLITES 4 +#define HABPACK_GNSS_LOCK 5 +#define HABPACK_VOLTAGE 6 + +/* Environmental Telemetry */ +#define HABPACK_INTERNAL_TEMPERATURE 10 +#define HABPACK_EXTERNAL_TEMPERATURE 11 +#define HABPACK_PRESSURE 12 +#define HABPACK_RELATIVE_HUMIDITY 13 +#define HABPACK_ABSOLUTE_HUMIDITY 14 + +/* Calling Beacon */ +#define HABPACK_DOWNLINK_FREQUENCY 20 +/* Common LoRa Mode */ +#define HABPACK_DOWNLINK_LORA_MODE 21 +/* _or_ Custom LoRa Parameters */ +#define HABPACK_DOWNLINK_LORA_IMPLICIT 22 +#define HABPACK_DOWNLINK_LORA_ERRORCODING 23 +#define HABPACK_DOWNLINK_LORA_BANDWIDTH 24 +#define HABPACK_DOWNLINK_LORA_SPREADING 25 +#define HABPACK_DOWNLINK_LORA_LDO 26 + +/* Uplink */ +#define HABPACK_UPLINK_COUNT 30 + +/* Landing Prediction */ +#define HABPACK_PREDICTED_LANDING_TIME 40 +#define HABPACK_PREDICTED_LANDING_POSITION 41 + +/* Multi-Position. TODO: Parsing unimplemented */ +#define HABPACK_MULTI_POS_POSITION_SCALING 60 +#define HABPACK_MULTI_POS_ALTITUDE_SCALING 61 +#define HABPACK_MULTI_POS_ARRAY 62 + +int BuildHABpackPacket(unsigned char *Packet, int Channel, struct TGPS *GPS); + +#endif /* __HABPACK_H__ */ + + diff --git a/tracker/lora.c b/tracker/lora.c index 0f1ba5c..038dffe 100755 --- a/tracker/lora.c +++ b/tracker/lora.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -27,6 +28,9 @@ #ifdef EXTRAS_PRESENT # include "ex_lora.h" #endif +#include "habpack.h" +#include "cutdown.h" +#include "pin.h" // RFM98 uint8_t currentMode = 0x81; @@ -67,13 +71,26 @@ struct TLoRaMode int Records, FileNumber; +void SetCS(int LoRaChannel, int Value) +{ + if (Config.LoRaDevices[LoRaChannel].CS > 0) + { + digitalWrite(Config.LoRaDevices[LoRaChannel].CS, Value); + } +} + void writeRegister(int Channel, uint8_t reg, uint8_t val) { unsigned char data[2]; data[0] = reg | 0x80; data[1] = val; + + SetCS(Channel, 0); + wiringPiSPIDataRW(Channel, data, 2); + + SetCS(Channel, 1); } uint8_t readRegister(int Channel, uint8_t reg) @@ -83,7 +100,13 @@ uint8_t readRegister(int Channel, uint8_t reg) data[0] = reg & 0x7F; data[1] = 0; + + SetCS(Channel, 0); + wiringPiSPIDataRW(Channel, data, 2); + + SetCS(Channel, 1); + val = data[1]; return val; @@ -97,8 +120,14 @@ void setMode(int LoRaChannel, uint8_t newMode) switch (newMode) { case RF98_MODE_TX: - writeRegister(LoRaChannel, REG_LNA, LNA_OFF_GAIN); // TURN LNA OFF FOR TRANSMITT + writeRegister(LoRaChannel, REG_LNA, LNA_OFF_GAIN); // TURN LNA OFF FOR TRANSMIT writeRegister(LoRaChannel, REG_PA_CONFIG, Config.LoRaDevices[LoRaChannel].Power); + if (Config.LoRaDevices[LoRaChannel].Power == 0xFF) + { + // Turn on additional amp for 20dBm. + // ** ONLY DO IF RADIO LAWS ALLOW, WHICH ESSENTIALLY MEANS YOU@RE A RADIO HAM WHERE YOUR COUNTRY ALLOWS AIRBORNE HAM TRANSMISSIONS. THIS EXCLUDES UK ** + writeRegister(LoRaChannel, REG_PA_DAC, PA_DAC_20); + } writeRegister(LoRaChannel, REG_OPMODE, newMode); currentMode = newMode; break; @@ -121,14 +150,17 @@ void setMode(int LoRaChannel, uint8_t newMode) if(newMode != RF98_MODE_SLEEP) { - // printf("Waiting for Mode Change\n"); - while(digitalRead(Config.LoRaDevices[LoRaChannel].DIO5) == 0) + if (Config.LoRaDevices[LoRaChannel].DIO5 > 0) + { + while(digitalRead(Config.LoRaDevices[LoRaChannel].DIO5) == 0) + { + } + } + else { - } - // printf("Mode change completed\n"); + delay(10); + } } - - return; } void SetLoRaFrequency(int LoRaChannel, double Frequency) @@ -141,13 +173,13 @@ void SetLoRaFrequency(int LoRaChannel, double Frequency) setMode(LoRaChannel, RF98_MODE_SLEEP); - FrequencyValue = (unsigned long)(Frequency * 7110656 / 434); + FrequencyValue = (unsigned long)(Frequency * (1.0 - Config.LoRaDevices[LoRaChannel].PPM/1000000.0) * 7110656 / 434); // printf("Channel %d frequency %lf FrequencyValue = %06lXh\n", LoRaChannel, Frequency, FrequencyValue); - writeRegister(LoRaChannel, 0x06, (FrequencyValue >> 16) & 0xFF); // Set frequency - writeRegister(LoRaChannel, 0x07, (FrequencyValue >> 8) & 0xFF); - writeRegister(LoRaChannel, 0x08, FrequencyValue & 0xFF); + writeRegister(LoRaChannel, REG_FRF_MSB, (FrequencyValue >> 16) & 0xFF); // Set frequency + writeRegister(LoRaChannel, REG_FRF_MID, (FrequencyValue >> 8) & 0xFF); + writeRegister(LoRaChannel, REG_FRF_LSB, FrequencyValue & 0xFF); } void setLoRaMode(int LoRaChannel) @@ -179,9 +211,21 @@ void setupRFM98(int LoRaChannel) if (Config.LoRaDevices[LoRaChannel].InUse) { // initialize the pins - printf("LoRa channel %d DIO0=%d DIO5=%d\n", LoRaChannel, Config.LoRaDevices[LoRaChannel].DIO0, Config.LoRaDevices[LoRaChannel].DIO5); pinMode(Config.LoRaDevices[LoRaChannel].DIO0, INPUT); - pinMode(Config.LoRaDevices[LoRaChannel].DIO5, INPUT); + if (Config.LoRaDevices[LoRaChannel].DIO5 > 0) + { + pinMode(Config.LoRaDevices[LoRaChannel].DIO5, INPUT); + } + if (Config.LoRaDevices[LoRaChannel].CS > 0) + { + pinMode(Config.LoRaDevices[LoRaChannel].CS, OUTPUT); + digitalWrite(Config.LoRaDevices[LoRaChannel].CS, 1); + } + if (Config.LoRaDevices[LoRaChannel].RST >= 0) + { + pinMode(Config.LoRaDevices[LoRaChannel].RST, OUTPUT); + digitalWrite(Config.LoRaDevices[LoRaChannel].RST, 1); + } if (wiringPiSPISetup(LoRaChannel, 500000) < 0) { @@ -204,6 +248,19 @@ void setupRFM98(int LoRaChannel) } } +void SwitchToLoRaMode(int LoRaChannel) +{ + setLoRaMode(LoRaChannel); + + SetLoRaParameters(LoRaChannel, + Config.LoRaDevices[LoRaChannel].ImplicitOrExplicit, + Config.LoRaDevices[LoRaChannel].ErrorCoding, + Config.LoRaDevices[LoRaChannel].Bandwidth, + Config.LoRaDevices[LoRaChannel]. SpreadingFactor, + Config.LoRaDevices[LoRaChannel].LowDataRateOptimize); + + // setupRFM98(LoRaChannel); +} void SendLoRaData(int LoRaChannel, unsigned char *buffer, int Length) { @@ -212,6 +269,13 @@ void SendLoRaData(int LoRaChannel, unsigned char *buffer, int Length) // printf("LoRa Channel %d Sending %d bytes\n", LoRaChannel, Length); + if (Config.LoRaDevices[LoRaChannel].InRTTYMode != 0) + { + // Set LoRa mode + SwitchToLoRaMode(LoRaChannel); + Config.LoRaDevices[LoRaChannel].InRTTYMode = 0; + } + Config.LoRaDevices[LoRaChannel].MillisSinceLastPacket = 0; setMode(LoRaChannel, RF98_MODE_STANDBY); @@ -226,9 +290,12 @@ void SendLoRaData(int LoRaChannel, unsigned char *buffer, int Length) { data[i+1] = buffer[i]; } + + SetCS(LoRaChannel, 0); + wiringPiSPIDataRW(LoRaChannel, data, Length+1); -// printf("Set Tx Mode\n"); + SetCS(LoRaChannel, 1); // Set the length. For implicit mode, since the length needs to match what the receiver expects, we have to set a value which is 255 for an SSDV packet writeRegister(LoRaChannel, REG_PAYLOAD_LENGTH, Config.LoRaDevices[LoRaChannel].PayloadLength ? Config.LoRaDevices[LoRaChannel].PayloadLength : Length); @@ -237,8 +304,166 @@ void SendLoRaData(int LoRaChannel, unsigned char *buffer, int Length) setMode(LoRaChannel, RF98_MODE_TX); Config.LoRaDevices[LoRaChannel].LoRaMode = lmSending; + Config.LoRaDevices[LoRaChannel].SendingRTTY = 0; } +void SwitchToFSKMode(int LoRaChannel) +{ + unsigned long FrequencyValue; + + uint8_t mode = readRegister(LoRaChannel, REG_OPMODE); + mode |= (1<<3); //set LF range + + if (mode & (1<<7)) //if in lora mode + { + writeRegister(LoRaChannel, REG_OPMODE,(mode & ~(uint8_t)7)); //set to sleep mode so fsk bit can be written + } + else + { + writeRegister(LoRaChannel, REG_OPMODE,(mode & ~(uint8_t)7) | 1); //set to standby mode so various settings can be written + } + + mode = readRegister(LoRaChannel, REG_OPMODE); + writeRegister(LoRaChannel, REG_OPMODE, mode & ~(uint8_t)(7<<5)); //set to FSK + + writeRegister(LoRaChannel, REG_LNA, LNA_OFF_GAIN); // TURN LNA OFF FOR TRANSMIT + writeRegister(LoRaChannel, REG_PA_CONFIG, Config.LoRaDevices[LoRaChannel].Power); + + // Frequency + FrequencyValue = (unsigned long)(Config.LoRaDevices[LoRaChannel].RTTYFrequency * (1.0 - Config.LoRaDevices[LoRaChannel].PPM/1000000.0) * 7110656 / 434); + writeRegister(LoRaChannel, REG_FRF_MSB, (FrequencyValue >> 16) & 0xFF); // Set frequency + writeRegister(LoRaChannel, REG_FRF_MID, (FrequencyValue >> 8) & 0xFF); + writeRegister(LoRaChannel, REG_FRF_LSB, FrequencyValue & 0xFF); + + //write modem config + writeRegister(LoRaChannel, REG_BITRATE_LSB, Config.LoRaDevices[LoRaChannel].FSKBitRate & 0xFF); + writeRegister(LoRaChannel, REG_BITRATE_MSB, (Config.LoRaDevices[LoRaChannel].FSKBitRate >> 8) & 0xFF); + writeRegister(LoRaChannel, REG_FDEV_LSB, (Config.LoRaDevices[LoRaChannel].RTTYShift / 122) & 0xFF); + writeRegister(LoRaChannel, REG_FDEV_MSB, 0); + writeRegister(LoRaChannel, REG_PREAMBLE_LSB_FSK, 0); // Preamble + writeRegister(LoRaChannel, REG_PREAMBLE_MSB_FSK, 0); + + // writeRegister(LoRaChannel, REG_PACKET_CONFIG1, 0x80); // variable length, no DC fix, no CRC, no addressing + writeRegister(LoRaChannel, REG_PACKET_CONFIG1, 0x00); // fixed length, no DC fix, no CRC, no addressing + writeRegister(LoRaChannel, REG_PAYLOAD_LENGTH_FSK, 0); + +} + +int FSKPacketSent(int LoRaChannel) +{ + return ((readRegister(LoRaChannel, REG_IRQ_FLAGS2) & 0x48) != 0); +} + +int FSKBufferLow(int LoRaChannel) +{ + return (readRegister(LoRaChannel, REG_IRQ_FLAGS2) & 0x20) == 0; +} + +void AddBytesToFSKBuffer(int LoRaChannel, int MaxBytes) +{ + unsigned char data[65], temp; + int i; + uint8_t BytesWritten = 0; + + if (Config.LoRaDevices[LoRaChannel].RTTYIndex < Config.LoRaDevices[LoRaChannel].RTTYLength) + { + data[BytesWritten++] = REG_FIFO | 0x80; + + // printf("*** %d of %d *** \n", Config.LoRaDevices[LoRaChannel].RTTYIndex, Config.LoRaDevices[LoRaChannel].RTTYLength); + + while((BytesWritten <= (MaxBytes-Config.LoRaDevices[LoRaChannel].FSKOverSample+1)) && + (Config.LoRaDevices[LoRaChannel].RTTYIndex < Config.LoRaDevices[LoRaChannel].RTTYLength)) + { + if (Config.LoRaDevices[LoRaChannel].RTTYMask < 0) //start bit + { + temp = 0xFF; + Config.LoRaDevices[LoRaChannel].RTTYMask++; + } + else if (Config.LoRaDevices[LoRaChannel].RTTYMask == 0) //start bit + { + temp = 0; + Config.LoRaDevices[LoRaChannel].RTTYMask = 1; + } + else if (Config.LoRaDevices[LoRaChannel].RTTYMask >= (1<= (1<<(Config.LoRaDevices[LoRaChannel].RTTYBitLength+2))) + { + Config.LoRaDevices[LoRaChannel].RTTYMask = 0; + Config.LoRaDevices[LoRaChannel].RTTYIndex++; + } + } + else //databits + { + if (Config.LoRaDevices[LoRaChannel].RTTYBuffer[Config.LoRaDevices[LoRaChannel].RTTYIndex] & Config.LoRaDevices[LoRaChannel].RTTYMask) + { + temp = 0xFF; + } + else + { + temp = 0x0; + } + Config.LoRaDevices[LoRaChannel].RTTYMask <<= 1; + } + + for (i = 0; i < Config.LoRaDevices[LoRaChannel].FSKOverSample; i++) + { + data[BytesWritten++] = temp; + } + } + + /* + LogMessage("*** ADDED %d BYTES ***\n", BytesWritten-1); + for (i = 1; i <= BytesWritten; i++) + { + printf("%c", data[i] ? '-' : '_'); + } + printf("\n"); + */ + + SetCS(LoRaChannel, 0); + wiringPiSPIDataRW(LoRaChannel, data, BytesWritten); + SetCS(LoRaChannel, 1); + + // printf("IRQ[%d] = %02X, MODE = %02X\n", LoRaChannel, readRegister(LoRaChannel, REG_IRQ_FLAGS2), readRegister(LoRaChannel, REG_OPMODE)); + } +} + +void SendLoRaRTTY(int LoRaChannel, unsigned char *buffer, int Length) +{ + // printf("LoRa Channel %d Sending %d bytes\n", LoRaChannel, Length); + + if (Config.LoRaDevices[LoRaChannel].InRTTYMode != 1) + { + // Set RTTY mode + SwitchToFSKMode(LoRaChannel); + Config.LoRaDevices[LoRaChannel].InRTTYMode = 1; + } + + Config.LoRaDevices[LoRaChannel].MillisSinceLastPacket = 0; + + // Fill in RTTY buffer + memcpy(Config.LoRaDevices[LoRaChannel].RTTYBuffer, buffer, Length); + Config.LoRaDevices[LoRaChannel].RTTYLength = Length; + Config.LoRaDevices[LoRaChannel].RTTYIndex = 0; + Config.LoRaDevices[LoRaChannel].RTTYMask = -Config.LoRaDevices[LoRaChannel].RTTYPreamble; + + // Set FIFO threshold + uint8_t r = readRegister(LoRaChannel, REG_FIFO_THRESH); + writeRegister(LoRaChannel, REG_FIFO_THRESH,(r&(~0x3F)) | 20); // 20 = FIFO threshold + + writeRegister(LoRaChannel, REG_OPMODE, 0x0B); // Tx mode + + // Populate FIFO + AddBytesToFSKBuffer(LoRaChannel, 64); + + // Set channel state + Config.LoRaDevices[LoRaChannel].LoRaMode = lmSending; + Config.LoRaDevices[LoRaChannel].SendingRTTY = 1; +} + + int BuildLoRaCall(unsigned char *TxLine, int LoRaChannel) { sprintf((char *)TxLine, "^^%s,%s,%d,%d,%d,%d,%d", @@ -278,11 +503,10 @@ int BuildLoRaPositionPacket(unsigned char *TxLine, int LoRaChannel, struct TGPS return sizeof(struct TBinaryPacket); } -int SendLoRaImage(int LoRaChannel) +void SendLoRaImage(int LoRaChannel, int RTTYMode) { unsigned char Buffer[256]; size_t Count; - int SentSomething = 0; int ResentPacket, Channel; Channel = LORA_CHANNEL + LoRaChannel; @@ -302,9 +526,14 @@ int SendLoRaImage(int LoRaChannel) printf("LORA%d: SSDV image %d packet %d of %d %s\r\n", LoRaChannel, Config.Channels[Channel].SSDVImageNumber, Config.Channels[Channel].SSDVPacketNumber+1, Config.Channels[Channel].SSDVNumberOfPackets, ResentPacket ? "** RESEND **" : ""); - SendLoRaData(LoRaChannel, Buffer+1, 255); - - SentSomething = 1; + if (RTTYMode) + { + SendLoRaRTTY(LoRaChannel, Buffer+1, 255); + } + else + { + SendLoRaData(LoRaChannel, Buffer+1, 255); + } } else { @@ -312,14 +541,23 @@ int SendLoRaImage(int LoRaChannel) Config.Channels[Channel].ImageFP = NULL; } } - - return SentSomething; + + if (Config.Channels[Channel].ImageFP == NULL) + { + // Nothing sent + if (Config.LoRaDevices[LoRaChannel].SpeedMode > 0) + { + memset(Buffer, '\0', 255); + SendLoRaData(LoRaChannel, Buffer, 255); + // printf("Sending NULL packet as time filler\n"); + } + } } int TDMTimeToSendOnThisChannel(int LoRaChannel, struct TGPS *GPS) { - // Can't send till we have the time! - if (GPS->Satellites > 0) + // Can't send till we have the time, or at least have had it at some point (hopefully Pi maintains a good clock after GPS loss)! + if (GPS->SecondsInDay > 0) { // Can't Tx twice at the same time if (GPS->SecondsInDay != Config.LoRaDevices[LoRaChannel].LastTxAt) @@ -331,24 +569,35 @@ int TDMTimeToSendOnThisChannel(int LoRaChannel, struct TGPS *GPS) if (CycleSeconds == Config.LoRaDevices[LoRaChannel].Slot) { Config.LoRaDevices[LoRaChannel].LastTxAt = GPS->SecondsInDay; - Config.LoRaDevices[LoRaChannel].SendRepeatedPacket = 0; + Config.LoRaDevices[LoRaChannel].SendPacketType = ptNormal; return 1; } if (Config.LoRaDevices[LoRaChannel].PacketRepeatLength && (CycleSeconds == Config.LoRaDevices[LoRaChannel].RepeatSlot)) { Config.LoRaDevices[LoRaChannel].LastTxAt = GPS->SecondsInDay; - Config.LoRaDevices[LoRaChannel].SendRepeatedPacket = 1; + Config.LoRaDevices[LoRaChannel].SendPacketType = ptBalloonRepeat; return 1; } if (Config.LoRaDevices[LoRaChannel].UplinkRepeatLength && (CycleSeconds == Config.LoRaDevices[LoRaChannel].UplinkSlot)) { Config.LoRaDevices[LoRaChannel].LastTxAt = GPS->SecondsInDay; - Config.LoRaDevices[LoRaChannel].SendRepeatedPacket = 2; + Config.LoRaDevices[LoRaChannel].SendPacketType = ptUplinkRepeat; return 1; } + if (CycleSeconds == Config.LoRaDevices[LoRaChannel].CallingSlot) + { + if (Config.LoRaDevices[LoRaChannel].CallingFrequency[0] && + Config.LoRaDevices[LoRaChannel].CallingCount && + (Config.LoRaDevices[LoRaChannel].PacketsSinceLastCall >= Config.LoRaDevices[LoRaChannel].CallingCount)) + { + Config.LoRaDevices[LoRaChannel].LastTxAt = GPS->SecondsInDay; + Config.LoRaDevices[LoRaChannel].SendPacketType = ptCallingMode; + return 1; + } + } } } @@ -357,8 +606,9 @@ int TDMTimeToSendOnThisChannel(int LoRaChannel, struct TGPS *GPS) int UplinkTimeToSendOnThisChannel(int LoRaChannel, struct TGPS *GPS) { - // Can't use time till we have it - if (GPS->Satellites > 0) + // Can't use time till we have synced from GPS + + if (GPS->SecondsInDay > 0) { long CycleSeconds; @@ -455,10 +705,11 @@ double FrequencyError(int Channel) Temp = Temp - 524288; } - // return - ((double)Temp * (1<<24) / 32000000.0) * (125000 / 500000.0); - return - ((double)Temp * (1<<24) / 32000000.0) * (BandwidthInKHz(Channel) / 500.0); + // return - ((double)Temp * (1<<24) / 32000000.0) * (BandwidthInKHz(Channel) / 500.0); <<-- Used Tx settings not Rx + return - ((double)Temp * (1<<24) / 32000000.0) * (LoRaModes[Config.LoRaDevices[Channel].UplinkMode].Bandwidth / 500.0); } + int receiveMessage(int LoRaChannel, unsigned char *message) { int i, Bytes, currentAddr, x; @@ -500,7 +751,13 @@ int receiveMessage(int LoRaChannel, unsigned char *message) writeRegister(LoRaChannel, REG_FIFO_ADDR_PTR, currentAddr); data[0] = REG_FIFO; + + SetCS(LoRaChannel, 0); + wiringPiSPIDataRW(LoRaChannel, data, Bytes+1); + + SetCS(LoRaChannel, 1); + for (i=0; i<=Bytes; i++) { message[i] = data[i+1]; @@ -515,6 +772,104 @@ int receiveMessage(int LoRaChannel, unsigned char *message) return Bytes; } +void ProcessCommandUplinkMessage(int LoRaChannel, struct TGPS *GPS, char *Message) +{ + // Process uplink message from gateway + char Command, Parameter, PayloadID[32]; + + printf("Message is '%s'\n", Message); + Message++; + DecryptMessage(Config.UplinkCode, Message); + + printf("Message is '*%s'\n", Message); + + GetString(PayloadID, &Message); + + if (strcmp(PayloadID, Config.Channels[LoRaChannel+LORA_CHANNEL].PayloadID) == 0) + { + Config.LoRaDevices[LoRaChannel].MessageCount++; + strcpy(Config.LoRaDevices[LoRaChannel].LastCommand, Message); + + printf("Uplink message for us = '%s'\n", Message); + Command = GetChar(&Message); + + if (Command == 'C') + { + // Cutdown + Parameter = GetChar(&Message); + + if (Parameter == 'N') + { + int CutdownPeriod; + + // Cutdown Now + CutdownPeriod = GetInteger(&Message); + + if (CutdownPeriod <= 0) + { + CutdownPeriod = Config.CutdownPeriod; + } + + printf("** MANUAL CUTDOWN FOR %d SECONDS **\n", CutdownPeriod); + + Cutdown(CutdownPeriod); + + GPS->CutdownStatus = csManual; + } + else if (Parameter == 'A') + { + // Cutdown at specified altitude + printf("Set cutdown altitude %sm\n", Message); + Config.CutdownAltitude = GetInteger(&Message); + } + } + else if (Command == 'P') + { + int Pin, Period; + // Control Specific Pin + + Pin = GetInteger(&Message); + Period = GetInteger(&Message); + + printf("** SET PIN %d FOR %d SECONDS **\n", Pin, Period); + + if ((Pin > 0) && (Period >= 0) && (Period <= 60)) + { + ControlPin(Pin, Period); + } + } + else if (Command == 'S') + { + int Pin, Period, Position; + // Control Specific Pin + + Pin = GetInteger(&Message); + Period = GetInteger(&Message); + Position = GetInteger(&Message); + + printf("** SERVO PIN %d FOR %d SECONDS POSITION %d **\n", Pin, Period, Position); + + if ((Pin > 0) && (Period >= 0) && (Period <= 60)) + { + ControlServo(Pin, Period, Position); + } + } + else if (Command == 'R') + { + // Run external command + + printf("** RUN COMMAND %s **\n", Message); + + + system(Message); + } + } + else + { + printf("Uplink message was for %s and not us\n", PayloadID); + } +} + void CheckForPacketOnListeningChannels(struct TGPS *GPS) { int LoRaChannel; @@ -628,6 +983,12 @@ void CheckForPacketOnListeningChannels(struct TGPS *GPS) printf("SSDV uplink message %s", Message); ProcessSSDVUplinkMessage(LORA_CHANNEL+LoRaChannel, Message); } + else if (Message[0] == '*') + { + // Uplink Command Device message + ProcessCommandUplinkMessage(LoRaChannel, GPS, (char *)Message); + } + # ifdef EXTRAS_PRESENT else if (ProcessExtraMessage(LoRaChannel, Message, Bytes, GPS)) { @@ -655,7 +1016,7 @@ int CheckForFreeChannel(struct TGPS *GPS) { if ((Config.LoRaDevices[LoRaChannel].LoRaMode != lmSending) || digitalRead(Config.LoRaDevices[LoRaChannel].DIO0)) { - // printf ("LoRa Channel %d is free\n", Channel); + // printf ("LoRa Channel %d is free\n", LoRaChannel); // Either not sending, or was but now it's sent. Clear the flag if we need to if (Config.LoRaDevices[LoRaChannel].LoRaMode == lmSending) { @@ -765,6 +1126,8 @@ void LoadLoRaConfig(FILE *fp, struct TConfig *Config) if (Config->LoRaDevices[LoRaChannel].Frequency[0]) { + Config->LoRaDevices[LoRaChannel].PPM = ReadFloat(fp, "LORA_PPM", LoRaChannel, 0, 0); + printf("LORA%d - frequency set to %s\n", LoRaChannel, Config->LoRaDevices[LoRaChannel].Frequency); Config->LoRaDevices[LoRaChannel].InUse = 1; Config->Channels[Channel].Enabled = 1; @@ -780,22 +1143,39 @@ void LoadLoRaConfig(FILE *fp, struct TConfig *Config) Config->LoRaDevices[LoRaChannel].DIO0 = ReadInteger(fp, "LORA_DIO0", LoRaChannel, 0, Config->LoRaDevices[LoRaChannel].DIO0); Config->LoRaDevices[LoRaChannel].DIO5 = ReadInteger(fp, "LORA_DIO5", LoRaChannel, 0, Config->LoRaDevices[LoRaChannel].DIO5); printf(" - DIO0=%d DIO5=%d\n", Config->LoRaDevices[LoRaChannel].DIO0, Config->LoRaDevices[LoRaChannel].DIO5); - + + // CS for those stupid boards that don't use standard Pi CS pins ... + Config->LoRaDevices[LoRaChannel].CS = ReadInteger(fp, "LORA_CS", LoRaChannel, 0, 0); + if (Config->LoRaDevices[LoRaChannel].CS > 0) + { + printf(" - **NON STANDARD** CS pin %d\n", Config->LoRaDevices[LoRaChannel].CS); + } + + Config->LoRaDevices[LoRaChannel].RST = ReadInteger(fp, "LORA_RST", LoRaChannel, 0, -1); + if (Config->LoRaDevices[LoRaChannel].RST >= 0) + { + printf(" - RST pin %d\n", Config->LoRaDevices[LoRaChannel].RST); + } + if (Config->Camera) { Config->Channels[Channel].ImageWidthWhenLow = ReadInteger(fp, "LORA_low_width", LoRaChannel, 0, 320); Config->Channels[Channel].ImageHeightWhenLow = ReadInteger(fp, "LORA_low_height", LoRaChannel, 0, 240); - printf (" - Low image size %d x %d pixels\n", Config->Channels[Channel].ImageWidthWhenLow, Config->Channels[Channel].ImageHeightWhenLow); Config->Channels[Channel].ImageWidthWhenHigh = ReadInteger(fp, "LORA_high_width", LoRaChannel, 0, 640); Config->Channels[Channel].ImageHeightWhenHigh = ReadInteger(fp, "LORA_high_height", LoRaChannel, 0, 480); - printf (" - High image size %d x %d pixels\n", Config->Channels[Channel].ImageWidthWhenHigh, Config->Channels[Channel].ImageHeightWhenHigh); Config->Channels[Channel].ImagePackets = ReadInteger(fp, "LORA_image_packets", LoRaChannel, 0, 4); - printf (" - 1 Telemetry packet every %d image packets\n", Config->Channels[Channel].ImagePackets); Config->Channels[Channel].ImagePeriod = ReadInteger(fp, "LORA_image_period", LoRaChannel, 0, 60); - printf (" - %d seconds between photographs\n", Config->Channels[Channel].ImagePeriod); + + if (Config->Camera != 5) + { + printf (" - Low image size %d x %d pixels\n", Config->Channels[Channel].ImageWidthWhenLow, Config->Channels[Channel].ImageHeightWhenLow); + printf (" - High image size %d x %d pixels\n", Config->Channels[Channel].ImageWidthWhenHigh, Config->Channels[Channel].ImageHeightWhenHigh); + printf (" - 1 Telemetry packet every %d image packets\n", Config->Channels[Channel].ImagePackets); + printf (" - %d seconds between photographs\n", Config->Channels[Channel].ImagePeriod); + } } else { @@ -829,8 +1209,9 @@ void LoadLoRaConfig(FILE *fp, struct TConfig *Config) } } - ReadBoolean(fp, "LORA_Binary", LoRaChannel, 0, &(Config->LoRaDevices[LoRaChannel].Binary)); - printf(" - Using %s Transmissions\n", Config->LoRaDevices[LoRaChannel].Binary ? "Binary" : "ASCII"); + ReadBoolean(fp, "LORA_HABPack", LoRaChannel, 0, &(Config->LoRaDevices[LoRaChannel].HABPack)); + ReadBoolean(fp, "LORA_Binary", LoRaChannel, 0, &(Config->LoRaDevices[LoRaChannel].Binary)); + printf(" - Using %s Transmissions\n", Config->LoRaDevices[LoRaChannel].HABPack ? "HABPack" : (Config->LoRaDevices[LoRaChannel].Binary ? "Binary" : "ASCII")); ReadBoolean(fp, "LORA_ListenOnly", LoRaChannel, 0, &(Config->LoRaDevices[LoRaChannel].ListenOnly)); if (Config->LoRaDevices[LoRaChannel].ListenOnly) @@ -942,8 +1323,45 @@ void LoadLoRaConfig(FILE *fp, struct TConfig *Config) if (Config->LoRaDevices[LoRaChannel].CallingCount) { printf(" - Calling Packet will be sent on %s every %d packets\n", Config->LoRaDevices[LoRaChannel].CallingFrequency, Config->LoRaDevices[LoRaChannel].CallingCount); + Config->LoRaDevices[LoRaChannel].CallingSlot = ReadInteger(fp, "LORA_Calling_Slot", LoRaChannel, 0, -1); + if (Config->LoRaDevices[LoRaChannel].CallingSlot >= 0) + { + printf(" - Calling Packet will be sent in slot %d\n", Config->LoRaDevices[LoRaChannel].CallingSlot); + } } - } + } + + // RTTY using LoRa chip + Config->LoRaDevices[LoRaChannel].RTTYBaudRate = ReadInteger(fp, "LORA_RTTY_Baud", LoRaChannel, 0, 0); + if (Config->LoRaDevices[LoRaChannel].RTTYBaudRate > 0) + { + Config->LoRaDevices[LoRaChannel].RTTYFrequency = ReadFloat(fp, "LORA_RTTY_Frequency", LoRaChannel, 0, atof(Config->LoRaDevices[LoRaChannel].Frequency)); + Config->LoRaDevices[LoRaChannel].RTTYShift = ReadInteger(fp, "LORA_RTTY_Shift", LoRaChannel, 0, 488); + printf(" - RTTY Enabled at %d baud, %dHz shift\n", Config->LoRaDevices[LoRaChannel].RTTYBaudRate, Config->LoRaDevices[LoRaChannel].RTTYShift); + + if (Config->LoRaDevices[LoRaChannel].RTTYBaudRate == 50) + { + Config->LoRaDevices[LoRaChannel].FSKBitRate = 40000; + Config->LoRaDevices[LoRaChannel].FSKOverSample = 2; + Config->LoRaDevices[LoRaChannel].RTTYBitLength = 7; + } + else + { + // 300 baud + Config->LoRaDevices[LoRaChannel].FSKBitRate = 13333; // 53333; + Config->LoRaDevices[LoRaChannel].FSKOverSample = 1; + Config->LoRaDevices[LoRaChannel].RTTYBitLength = 8; + } + + Config->LoRaDevices[LoRaChannel].RTTYCount = ReadInteger(fp, "LORA_RTTY_Count", LoRaChannel, 0, 1); + Config->LoRaDevices[LoRaChannel].RTTYEvery = ReadInteger(fp, "LORA_RTTY_Every", LoRaChannel, 0, 1); + Config->LoRaDevices[LoRaChannel].RTTYPreamble = ReadInteger(fp, "LORA_RTTY_Preamble", LoRaChannel, 0, 8); + + printf(" - %d RTTY sentences for every %d LoRa packets\n", Config->LoRaDevices[LoRaChannel].RTTYCount, Config->LoRaDevices[LoRaChannel].RTTYEvery); + } + + Config->LoRaDevices[LoRaChannel].InRTTYMode = -1; + # ifdef EXTRAS_PRESENT LoadExtraLoRaConfig(fp, Config, LoRaChannel); # endif @@ -954,6 +1372,31 @@ void LoadLoRaConfig(FILE *fp, struct TConfig *Config) } } } + + +void CheckFSKBuffers(struct TGPS *GPS) +{ + int LoRaChannel; + + for (LoRaChannel=0; LoRaChannel<=1; LoRaChannel++) + { + if ((Config.LoRaDevices[LoRaChannel].LoRaMode == lmSending) && Config.LoRaDevices[LoRaChannel].SendingRTTY) + { + // Check if packet sent + if (FSKPacketSent(LoRaChannel)) + { + // printf("*** PACKET SENT ***\n"); + Config.LoRaDevices[LoRaChannel].LoRaMode = lmIdle; + Config.LoRaDevices[LoRaChannel].SendingRTTY = 0; + } + else if (FSKBufferLow(LoRaChannel)) + { + AddBytesToFSKBuffer(LoRaChannel, 64 - 20); + } + } + } +} + void *LoRaLoop(void *some_void_ptr) { @@ -966,19 +1409,25 @@ void *LoRaLoop(void *some_void_ptr) for (LoRaChannel=0; LoRaChannel<2; LoRaChannel++) { - setupRFM98(LoRaChannel); - if (Config.LoRaDevices[LoRaChannel].SpeedMode == 2) + if (Config.LoRaDevices[LoRaChannel].InUse) { - startReceiving(LoRaChannel); + printf("LoRa channel %d DIO0=%d DIO5=%d, CS=%d\n", LoRaChannel, Config.LoRaDevices[LoRaChannel].DIO0, Config.LoRaDevices[LoRaChannel].DIO5, Config.LoRaDevices[LoRaChannel].CS); + setupRFM98(LoRaChannel); + if (Config.LoRaDevices[LoRaChannel].SpeedMode == 2) + { + startReceiving(LoRaChannel); + } + + Config.LoRaDevices[LoRaChannel].PacketsSinceLastCall = Config.LoRaDevices[LoRaChannel].CallingCount; // So we do the calling channel first } - - Config.LoRaDevices[LoRaChannel].PacketsSinceLastCall = Config.LoRaDevices[LoRaChannel].CallingCount; // So we do the calling channel first } while (1) { delay(LoopMS); // To stop this loop gobbling up CPU + CheckFSKBuffers(GPS); + CheckForPacketOnListeningChannels(GPS); LoRaChannel = CheckForFreeChannel(GPS); // 0 or 1 if there's a free channel and we should be sending on that channel now @@ -989,6 +1438,8 @@ void *LoRaLoop(void *some_void_ptr) Channel = LoRaChannel + LORA_CHANNEL; + Config.LoRaDevices[LoRaChannel].SendPacketType = ptNormal; + if (Config.LoRaDevices[LoRaChannel].ReturnStateAfterCall) { double Frequency; @@ -1004,18 +1455,26 @@ void *LoRaLoop(void *some_void_ptr) Config.LoRaDevices[LoRaChannel].Bandwidth, Config.LoRaDevices[LoRaChannel]. SpreadingFactor, Config.LoRaDevices[LoRaChannel].LowDataRateOptimize); - printf("Reset after Uplink Mode\n"); + // printf("Reset after Uplink Mode\n"); + } + + // Calling mode needed ? + if ((Config.LoRaDevices[LoRaChannel].CycleTime <= 0) && + Config.LoRaDevices[LoRaChannel].CallingFrequency[0] && + Config.LoRaDevices[LoRaChannel].CallingCount && + (Config.LoRaDevices[LoRaChannel].PacketsSinceLastCall >= Config.LoRaDevices[LoRaChannel].CallingCount)) + { + Config.LoRaDevices[LoRaChannel].SendPacketType = ptCallingMode; } - - // Now decide what, if anything, to send + // Now decide what, if anything, to send // Handle deliberate delays between packets if ((Config.LoRaDevices[LoRaChannel].PacketEveryMilliSeconds > 0) && ((Config.LoRaDevices[LoRaChannel].MillisSinceLastPacket += LoopMS) < Config.LoRaDevices[LoRaChannel].PacketEveryMilliSeconds)) { // Deliberate delay, and we're not there yet // DO NOTHING! } - else if (Config.LoRaDevices[LoRaChannel].SendRepeatedPacket == 2) + else if (Config.LoRaDevices[LoRaChannel].SendPacketType == ptUplinkRepeat) { printf("Repeating uplink packet of %d bytes\n", Config.LoRaDevices[LoRaChannel].UplinkRepeatLength); @@ -1023,7 +1482,7 @@ void *LoRaLoop(void *some_void_ptr) Config.LoRaDevices[LoRaChannel].UplinkRepeatLength = 0; } - else if (Config.LoRaDevices[LoRaChannel].SendRepeatedPacket == 1) + else if (Config.LoRaDevices[LoRaChannel].SendPacketType == ptBalloonRepeat) { printf("Repeating balloon packet of %d bytes\n", Config.LoRaDevices[LoRaChannel].PacketRepeatLength); @@ -1031,16 +1490,14 @@ void *LoRaLoop(void *some_void_ptr) Config.LoRaDevices[LoRaChannel].PacketRepeatLength = 0; } - else if (Config.LoRaDevices[LoRaChannel].CallingFrequency[0] && - Config.LoRaDevices[LoRaChannel].CallingCount && - (Config.LoRaDevices[LoRaChannel].PacketsSinceLastCall >= Config.LoRaDevices[LoRaChannel].CallingCount)) + else if (Config.LoRaDevices[LoRaChannel].SendPacketType == ptCallingMode) { int PacketLength; double Frequency; sscanf(Config.LoRaDevices[LoRaChannel].CallingFrequency, "%lf", &Frequency); SetLoRaFrequency(LoRaChannel, Frequency); - printf("Calling frequency is %lf\n", Frequency); + // printf("Calling frequency is %lf\n", Frequency); SetLoRaParameters(LoRaChannel, EXPLICIT_MODE, ERROR_CODING_4_8, BANDWIDTH_41K7, SPREADING_11, 0); // 0x08); @@ -1055,15 +1512,45 @@ void *LoRaLoop(void *some_void_ptr) } else { - int MaxImagePackets; - - if ((Config.Channels[Channel].SendMode == 0) || (Config.Channels[Channel].ImagePackets == 0)) + int MaxImagePackets, DoRTTY, PacketLength; + + + DoRTTY = 0; + + if ((Config.LoRaDevices[LoRaChannel].RTTYBaudRate > 0) && (Config.LoRaDevices[LoRaChannel].RTTYCount > 0)) + { + DoRTTY = Config.LoRaDevices[LoRaChannel].RTTYPacketIndex < Config.LoRaDevices[LoRaChannel].RTTYCount; + } + + + if (DoRTTY) { - int PacketLength; + if ((Config.Channels[Channel].SendMode == 0) || (Config.Channels[Channel].ImagePackets == 0) || (Config.LoRaDevices[LoRaChannel].RTTYEvery > 0)) + { + PacketLength = BuildSentence(Sentence, Channel, GPS); + LogMessage("RTTY%d: %s", LoRaChannel, Sentence); + + WriteTelemetryLog((char *)Sentence); + + SendLoRaRTTY(LoRaChannel, Sentence, PacketLength); + Config.LoRaDevices[LoRaChannel].PacketsSinceLastCall++; + } + else + { + SendLoRaImage(LoRaChannel, 1); + } + } + else if ((Config.Channels[Channel].SendMode == 0) || (Config.Channels[Channel].ImagePackets == 0)) + { // Telemetry packet - if (Config.LoRaDevices[LoRaChannel].Binary) + if (Config.LoRaDevices[LoRaChannel].HABPack) + { + PacketLength = BuildHABpackPacket(Sentence, Channel, GPS); + printf("LoRa%d: HABPack %d bytes\n", LoRaChannel, PacketLength); + } + else if (Config.LoRaDevices[LoRaChannel].Binary) { PacketLength = BuildLoRaPositionPacket(Sentence, LoRaChannel, GPS); printf("LoRa%d: Binary packet %d bytes\n", LoRaChannel, PacketLength); @@ -1080,7 +1567,7 @@ void *LoRaLoop(void *some_void_ptr) LogMessage("LORA%d: %s", LoRaChannel, Sentence); if (Config.EnableTelemetryLogging) { - WriteLog("telemetry.txt", (char *)Sentence); + WriteTelemetryLog((char *)Sentence); } } @@ -1092,8 +1579,16 @@ void *LoRaLoop(void *some_void_ptr) { // Image packet - SendLoRaImage(LoRaChannel); + SendLoRaImage(LoRaChannel, 0); } + + if ((Config.LoRaDevices[LoRaChannel].RTTYBaudRate > 0) && (Config.LoRaDevices[LoRaChannel].RTTYCount > 0)) + { + if (++Config.LoRaDevices[LoRaChannel].RTTYPacketIndex >= (Config.LoRaDevices[LoRaChannel].RTTYCount + Config.LoRaDevices[LoRaChannel].RTTYEvery)) + { + Config.LoRaDevices[LoRaChannel].RTTYPacketIndex = 0; + } + } if (Config.Channels[Channel].ImagePackets == 0) { diff --git a/tracker/lora.h b/tracker/lora.h index f493742..91df9b9 100755 --- a/tracker/lora.h +++ b/tracker/lora.h @@ -1,11 +1,21 @@ #define REG_FIFO 0x00 +#define REG_BITRATE_MSB 0x02 +#define REG_BITRATE_LSB 0x03 +#define REG_FDEV_MSB 0x04 +#define REG_FDEV_LSB 0x05 +#define REG_FRF_MSB 0x06 +#define REG_FRF_MID 0x07 +#define REG_FRF_LSB 0x08 #define REG_FIFO_ADDR_PTR 0x0D #define REG_FIFO_TX_BASE_AD 0x0E #define REG_FIFO_RX_BASE_AD 0x0F #define REG_RX_NB_BYTES 0x13 #define REG_OPMODE 0x01 #define REG_FIFO_RX_CURRENT_ADDR 0x10 +#define REG_IRQ_FLAGS_MASK 0x11 #define REG_IRQ_FLAGS 0x12 +#define REG_IRQ_FLAGS1 0x3E +#define REG_IRQ_FLAGS2 0x3F #define REG_PACKET_SNR 0x19 #define REG_PACKET_RSSI 0x1A #define REG_CURRENT_RSSI 0x1B @@ -15,11 +25,16 @@ #define REG_MODEM_CONFIG2 0x1E #define REG_MODEM_CONFIG3 0x26 #define REG_PAYLOAD_LENGTH 0x22 -#define REG_IRQ_FLAGS_MASK 0x11 #define REG_HOP_PERIOD 0x24 #define REG_FREQ_ERROR 0x28 #define REG_DETECT_OPT 0x31 #define REG_DETECTION_THRESHOLD 0x37 +#define REG_PREAMBLE_MSB_FSK 0x25 +#define REG_PREAMBLE_LSB_FSK 0x26 +#define REG_SYNC_CONFIG 0x27 +#define REG_PACKET_CONFIG1 0x30 +#define REG_PAYLOAD_LENGTH_FSK 0x32 +#define REG_FIFO_THRESH 0x35 // MODES #define RF98_MODE_RX_CONTINUOUS 0x85 @@ -70,6 +85,8 @@ #define PA_MAX_UK 0x88 #define PA_OFF_BOOST 0x00 #define RFO_MIN 0x00 +#define REG_PA_DAC 0x4D +#define PA_DAC_20 0x87 // LOW NOISE AMPLIFIER #define REG_LNA 0x0C diff --git a/tracker/misc.c b/tracker/misc.c index 9c49f39..aed9d88 100755 --- a/tracker/misc.c +++ b/tracker/misc.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -38,11 +39,39 @@ char Hex(unsigned char Character) return HexTable[Character & 15]; } -void WriteLog(char *FileName, char *Buffer) +void WriteGPSLog(char *Buffer) +{ + if (Config.EnableGPSLogging) + { + FILE *fp = NULL; + + if ((fp = fopen("gps.txt", "at")) != NULL) + { + fputs(Buffer, fp); + fclose(fp); + } + } +} + +void WriteTelemetryLog(char *Buffer) { - FILE *fp; + if (Config.EnableTelemetryLogging) + { + FILE *fp = NULL; + + if ((fp = fopen("telemetry.txt", "at")) != NULL) + { + fputs(Buffer, fp); + fclose(fp); + } + } +} + +void WritePredictionLog(char *Buffer) +{ + FILE *fp = NULL; - if ((fp = fopen(FileName, "at")) != NULL) + if ((fp = fopen("prediction.txt", "at")) != NULL) { fputs(Buffer, fp); fclose(fp); @@ -219,6 +248,10 @@ void StartNewFileIfNeeded(int Channel) RecordCount = MAX_SSDV_PACKETS; } + // FIFO for SSDV buffers + memcpy(&(Config.Channels[Channel].SSDVPackets[2]), &(Config.Channels[Channel].SSDVPackets[1]), sizeof(Config.Channels[Channel].SSDVPackets[2])); + memcpy(&(Config.Channels[Channel].SSDVPackets[1]), &(Config.Channels[Channel].SSDVPackets[0]), sizeof(Config.Channels[Channel].SSDVPackets[1])); + // Now fill in list of un-sent packets for (i=0; i MAX_LEN) - // { - // Buffer[MAX_LEN-2] = '.'; - // Buffer[MAX_LEN-1] = '.'; - // Buffer[MAX_LEN] = 0; - // } - if (Buffer[strlen(Buffer)-1] == '\n') { Buffer[strlen(Buffer)-1] = '\0'; @@ -758,7 +815,7 @@ int BuildSentence(unsigned char *TxLine, int Channel, struct TGPS *GPS) static int FirstTime=1; int LoRaChannel; int ShowFields; - char TimeBuffer[12], ExtraFields1[20], ExtraFields2[20], ExtraFields3[20], ExtraFields4[64], ExtraFields5[32], ExtraFields6[32], *ExtraFields7; + char TimeBuffer[12], ExtraFields1[20], ExtraFields2[20], ExtraFields3[20], ExtraFields4[64], ExtraFields5[32], ExtraFields6[32], ExtraFields7[32], *ExtraFields8, Sentence[512], FieldList[32]; if (FirstTime) { @@ -777,10 +834,12 @@ int BuildSentence(unsigned char *TxLine, int Channel, struct TGPS *GPS) ExtraFields4[0] = '\0'; ExtraFields5[0] = '\0'; ExtraFields6[0] = '\0'; + ExtraFields7[0] = '\0'; - ExtraFields7 = ""; + ExtraFields8 = ""; - if (ShowFields) printf("%s: ID,Ctr,Time,Lat,Lon,Alt,Sped,Head,Sats,Int.Temp", Channels[Channel]); + strcpy(FieldList, ",0123456A"); + if (ShowFields) printf("%s: ID,Ctr,Time,Lat,Lon,Alt,Sats,Int.Temp", Channels[Channel]); // Battery voltage and current, if available @@ -793,6 +852,7 @@ int BuildSentence(unsigned char *TxLine, int Channel, struct TGPS *GPS) // Pi A or B. Only Battery Voltage on the PITS sprintf(ExtraFields1, ",%.3f", GPS->BatteryVoltage); + strcat(FieldList, "9"); if (ShowFields) printf(",Volts"); } else @@ -800,6 +860,7 @@ int BuildSentence(unsigned char *TxLine, int Channel, struct TGPS *GPS) // Pi A+ or B+ (V1 or V2 or V3). Full ADC for voltage and current sprintf(ExtraFields1, ",%.1f,%.3f", GPS->BatteryVoltage, GPS->BoardCurrent); + strcat(FieldList, "9P"); if (ShowFields) printf(",Volts,Current"); } @@ -807,11 +868,19 @@ int BuildSentence(unsigned char *TxLine, int Channel, struct TGPS *GPS) if (Config.EnableBME280) { sprintf(ExtraFields2, ",%.1f,%.0f,%0.1f", GPS->BMP180Temperature, GPS->Pressure, GPS->Humidity); + strcat(FieldList, "QRS"); if (ShowFields) printf(",BME.Temp,Pressure,Humidity"); } + else if (Config.EnableMS5611) + { + sprintf(ExtraFields2, ",%.1f,%.1f", GPS->BMP180Temperature, GPS->Pressure); + strcat(FieldList, "QR"); + if (ShowFields) printf(",BMP.Temp,Pressure"); + } else if (Config.EnableBMP085) { sprintf(ExtraFields2, ",%.1f,%.0f", GPS->BMP180Temperature, GPS->Pressure); + strcat(FieldList, "QR"); if (ShowFields) printf(",BMP.Temp,Pressure"); } @@ -819,6 +888,7 @@ int BuildSentence(unsigned char *TxLine, int Channel, struct TGPS *GPS) if (GPS->DS18B20Count > 1) { sprintf(ExtraFields3, ",%3.1f", GPS->DS18B20Temperature[Config.ExternalDS18B20]); + strcat(FieldList, "B"); if (ShowFields) printf(",Ext.Temp"); } @@ -831,6 +901,7 @@ int BuildSentence(unsigned char *TxLine, int Channel, struct TGPS *GPS) GPS->PredictedLongitude, GPS->PredictedLandingSpeed, GPS->TimeTillLanding); + strcat(FieldList, "TCDUV"); if (ShowFields) printf(",CDA,Pred.Lat,Pred.Lon,Pred.Land,Pred.TTL"); } @@ -844,12 +915,14 @@ int BuildSentence(unsigned char *TxLine, int Channel, struct TGPS *GPS) sprintf(ExtraFields5, ",%d,%d,%d", Config.LoRaDevices[LoRaChannel].LastPacketRSSI, Config.LoRaDevices[LoRaChannel].LastPacketSNR, Config.LoRaDevices[LoRaChannel].PacketCount); + strcat(FieldList, "GFH"); if (ShowFields) printf(",RSSI,SNR,Packets"); } if (Config.LoRaDevices[LoRaChannel].EnableMessageStatus) { - sprintf(ExtraFields6, ",%s", Config.LoRaDevices[LoRaChannel].LastCommand); + sprintf(ExtraFields6, ",%c", Config.LoRaDevices[LoRaChannel].LastCommand[0]); + strcat(FieldList, "W"); if (ShowFields) printf(",Command"); } @@ -889,7 +962,7 @@ int BuildSentence(unsigned char *TxLine, int Channel, struct TGPS *GPS) if (line[0]) { line[strcspn(line, "\n")] = '\0'; - sprintf(ExternalFields, ",%s", line); + sprintf(ExternalFields, ",%.90s", line); } fseek(ExternalFile, 0, SEEK_END); // clearerr(ExternalFile); @@ -897,10 +970,18 @@ int BuildSentence(unsigned char *TxLine, int Channel, struct TGPS *GPS) } } + + if (Config.EnableCutdown) + { + sprintf(ExtraFields7, ",%d", GPS->CutdownStatus); + strcat(FieldList, "E"); + if (ShowFields) printf(",CutdownStatus"); + } + // Bouy mode or normal mode ? if ((Config.BuoyModeAltitude > 0) && (GPS->Altitude < Config.BuoyModeAltitude)) { - sprintf((char *)TxLine, "$$%s,%d,%s,%7.5lf,%7.5lf", + sprintf(Sentence, "$$%s,%d,%s,%7.5lf,%7.5lf", Config.Channels[Channel].PayloadID, Config.Channels[Channel].SentenceCounter, TimeBuffer, @@ -910,20 +991,20 @@ int BuildSentence(unsigned char *TxLine, int Channel, struct TGPS *GPS) else { #ifdef EXTRAS_PRESENT - ExtraFields7 = misc_get_sentence_fields(GPS, ShowFields); + ExtraFields8 = misc_get_sentence_fields(GPS, ShowFields); #endif if (ShowFields) printf("\n"); - sprintf((char *)TxLine, "$$%s,%d,%s,%7.5lf,%7.5lf,%5.5" PRId32 ",%d,%d,%d,%3.1f%s%s%s%s%s%s%s%s", + snprintf(Sentence, 512, "$$%.15s,%d,%.9s,%7.5lf,%7.5lf,%5.5" PRId32 ",%d,%3.1f%.12s%.20s%.20s%.40s%.90s%.20s%.10s%.10s%.40s%s", Config.Channels[Channel].PayloadID, Config.Channels[Channel].SentenceCounter, TimeBuffer, GPS->Latitude, GPS->Longitude, GPS->Altitude, - (GPS->Speed * 13) / 7, - GPS->Direction, + + GPS->Satellites, GPS->DS18B20Temperature[(GPS->DS18B20Count > 1) ? (1-Config.ExternalDS18B20) : 0], ExtraFields1, @@ -933,10 +1014,12 @@ int BuildSentence(unsigned char *TxLine, int Channel, struct TGPS *GPS) ExternalFields, ExtraFields5, ExtraFields6, - ExtraFields7); + ExtraFields7, + ExtraFields8, + Config.SendFieldList ? FieldList : ""); } - AppendCRC((char *)TxLine); + AppendCRC(Sentence); // Separate sentence for landing prediction ? if (Config.PredictionID[0]) @@ -952,7 +1035,13 @@ int BuildSentence(unsigned char *TxLine, int Channel, struct TGPS *GPS) GPS->PredictedLongitude, 0); AppendCRC((char *)PredictionPayload); - strcat((char *)TxLine, PredictionPayload); + + strcpy((char *)TxLine, PredictionPayload); + strcat((char *)TxLine, Sentence); + } + else + { + strcpy((char *)TxLine, Sentence); } return strlen((char *)TxLine) + 1; @@ -970,4 +1059,73 @@ int FixDirection180(int Angle) } return Angle; -} \ No newline at end of file +} + +void SetupPWMFrequency(int Pin, int Frequency) +{ +// return softPwmCreate(Pin, 0, 100); + gpioSetPWMfrequency(Pin, Frequency); + // gpioServo(18, 2000); +} + +void ControlPWMOutput(int Pin, int Period) +{ +// softPwmWrite (Pin, Level); + gpioServo(Pin, Period); +} + +void DecryptMessage(char *Code, char *Message) +{ + int i, Len; + + Len = strlen(Code); + + if (Len > 0) + { + printf("Decoding ...\n"); + i = 0; + while (*Message) + { + *Message = (*Message ^ Code[i]) & 0x7F; + Message++; + i = (i + 1) % Len; + } + } +} + +char GetChar(char **Message) +{ + return *((*Message)++); +} + +void GetString(char *Field, char **Message) +{ + while (**Message && (**Message != '/')) + { + *Field++ = *((*Message)++); + } + + *Field = 0; + if (**Message) + { + (*Message)++; + } +} + +int32_t GetInteger(char **Message) +{ + char Temp[32]; + + GetString(Temp, Message); + + return atoi(Temp); +} + +double GetFloat(char **Message) +{ + char Temp[32]; + + GetString(Temp, Message); + + return atof(Temp); +} diff --git a/tracker/misc.h b/tracker/misc.h index b81a81d..bcad73a 100755 --- a/tracker/misc.h +++ b/tracker/misc.h @@ -6,12 +6,17 @@ typedef enum {lmIdle, lmListening, lmSending} tLoRaMode; +typedef enum {ptNormal, ptCallingMode, ptBalloonRepeat, ptUplinkRepeat} tPacketType; + struct TLoRaDevice { int InUse; int DIO0; int DIO5; + int CS; + int RST; char Frequency[8]; + float PPM; int SpeedMode; int Power; int PayloadLength; @@ -25,6 +30,7 @@ struct TLoRaDevice int RepeatSlot; int UplinkSlot; int Binary; + int HABPack; int LastTxAt; int LastRxAt; int AirCount; @@ -35,10 +41,12 @@ struct TLoRaDevice unsigned char UplinkPacket[256]; int PacketRepeatLength; int UplinkRepeatLength; - int SendRepeatedPacket; + tPacketType SendPacketType; tLoRaMode LoRaMode; + int SendingRTTY; char CallingFrequency[8]; int CallingCount; + int CallingSlot; int PacketsSinceLastCall; int ReturnStateAfterCall; @@ -63,6 +71,23 @@ struct TLoRaDevice int LastPacketSNR; int PacketCount; int ListenOnly; // True for listen-only payload that waits for an uplink before responding (or times out and sends anyway) + + // RTTY settings + int RTTYBaudRate; + double RTTYFrequency; + int RTTYShift; + int RTTYCount; + int RTTYEvery; + int RTTYPacketIndex; + int RTTYBitLength; + int InRTTYMode; + unsigned int FSKBitRate; + int FSKOverSample; + char RTTYBuffer[256]; + int RTTYIndex; + int RTTYMask; + int RTTYLength; + int RTTYPreamble; # ifdef EXTRAS_PRESENT # include "ex_misc_lora.h" @@ -155,6 +180,7 @@ struct TConfig // Extra devices int EnableBMP085; int EnableBME280; + int EnableMS5611; int ExternalDS18B20; // Logging @@ -167,6 +193,8 @@ struct TConfig int LED_Warn; // GPS Settings + int GPSModel; + int ShowGPS; int SDA; int SCL; char GPSDevice[64]; @@ -202,7 +230,6 @@ struct TConfig // Landing prediction int EnableLandingPrediction; int32_t TargetAltitude; - int32_t LandingAltitude; float cd_area; float payload_weight; @@ -210,6 +237,29 @@ struct TConfig // External data file (read into telemetry) char ExternalDataFileName[100]; + + // Lights and buzzers + int BlinkenLight; + int FlashBelow; + int Flashing; + int PiezoPin; + int WhistleBelow; + + // Cutdown + int EnableCutdown; // 1 = Enable cutdown tests. Adds status to telemetry + int CutdownPin; // WiringPi pin for cutdown output. Typically drives nichrome via MOSFET. No support yet for servo cutdown. + float CutdownLongitude, CutdownLatitude; // Used to set where cutdown will fire to limit distance from launch + int32_t MinCutdownAltitude; // Cutdown will not fire below this altitude (except for manual uplink) + int32_t CutdownAltitude; // Cut down above this altitude + unsigned long CutdownTimeSinceLaunch; // Cutdown after this flight timne in seconds + int CutdownPeriod; // Time that cutdown is triggered for (manual uplink can override) + char CutdownTest[16]; // Strting mask that, with lat/lon settings, controls where cutdown will trigger by position + int CutdownBurst; // 1 = cutdown once burst detected + + // Uplink + char UplinkCode[32]; + + int SendFieldList; # ifdef EXTRAS_PRESENT # include "ex_misc_config.h" @@ -219,7 +269,9 @@ struct TConfig extern struct TConfig Config; char Hex(unsigned char Character); -void WriteLog(char *FileName, char *Buffer); +void WriteGPSLog(char *Buffer); +void WriteTelemetryLog(char *Buffer); +void WritePredictionLog(char *Buffer); short open_i2c(int address); int FileExists(char *filename); int ReadBooleanFromString(FILE *fp, char *keyword, char *searchword); @@ -227,6 +279,7 @@ int ReadBoolean(FILE *fp, char *keyword, int Channel, int NeedValue, int *Result void ReadString(FILE *fp, char *keyword, int Channel, char *Result, int Length, int NeedValue); int ReadCameraType(FILE *fp, char *keyword); int ReadInteger(FILE *fp, char *keyword, int Channel, int NeedValue, int DefaultValue); +char ReadCharacter(FILE *fp, char *keyword); double ReadFloat(FILE *fp, char *keyword, int Channel, int NeedValue, double DefaultValue); void AppendCRC(char *Temp); void LogMessage(const char *format, ...); @@ -241,3 +294,10 @@ int GetBoardType(int *i2cChannel); int NoMoreSSDVPacketsToSend(int Channel); int BuildSentence(unsigned char *TxLine, int Channel, struct TGPS *GPS); int FixDirection180(int Angle); +void SetupPWMFrequency(int Pin, int Frequency); +void ControlPWMOutput(int Pin, int Period); +void DecryptMessage(char *Code, char *Message); +char GetChar(char **Message); +void GetString(char *Field, char **Message); +int32_t GetInteger(char **Message); +double GetFloat(char **Message); diff --git a/tracker/pin.c b/tracker/pin.c new file mode 100755 index 0000000..3178b16 --- /dev/null +++ b/tracker/pin.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gps.h" +#include "misc.h" +#include "pin.h" + +int ControlPinNumber=0; +int ControlPeriod=0; +int ControlPinValue=0; +int UsePWM=0; + +void ControlPin(int Pin, int Period) +{ + ControlPinNumber = Pin; + ControlPeriod = Period; + UsePWM = 0; +} + +void ControlServo(int Pin, int Period, int Value) +{ + ControlPinNumber = Pin; + ControlPeriod = Period; + ControlPinValue = Value; + UsePWM = 1; +} + +void *PinLoop(void *some_void_ptr) +{ + while (1) + { + if ((ControlPinNumber > 0) && (ControlPeriod >= 0) && (ControlPeriod <= 60)) + { + if (UsePWM) + { + pinMode (ControlPinNumber, PWM_OUTPUT); + pwmWrite (ControlPinNumber, ControlPinValue); + if (ControlPeriod > 0) + { + sleep(ControlPeriod); + pwmWrite (ControlPinNumber, 0); + } + } + else + { + pinMode (ControlPinNumber, OUTPUT); + digitalWrite (ControlPinNumber, 1); + if (ControlPeriod > 0) + { + sleep(ControlPeriod); + digitalWrite (ControlPinNumber, 0); + } + } + + ControlPinNumber = 0; + } + + sleep(1); + } + + return 0; +} diff --git a/tracker/pin.h b/tracker/pin.h new file mode 100755 index 0000000..5cc3f99 --- /dev/null +++ b/tracker/pin.h @@ -0,0 +1,4 @@ +void ControlPin(int Pin, int Period); +void ControlServo(int Pin, int Period, int Value); +void *PinLoop(void *some_void_ptr); + diff --git a/tracker/prediction.c b/tracker/prediction.c index 253a239..911d537 100755 --- a/tracker/prediction.c +++ b/tracker/prediction.c @@ -94,11 +94,9 @@ void *PredictionLoop(void *some_void_ptr) { sleep(POLL_PERIOD); - if (GPS->Satellites >= 4) + if ((GPS->Satellites >= 4) && (GPS->Latitude >= -90) && (GPS->Latitude <= 90) && (GPS->Longitude >= -180) && (GPS->Longitude <= 180)) { int Slot; - double Latitude, Longitude, TimeInSlot, DescentRate, TimeTillLanding; - unsigned long Altitude, DistanceInSlot; char Temp[200]; if ((GPS->FlightMode >= fmLaunched) && (GPS->FlightMode < fmLanded)) @@ -119,49 +117,22 @@ void *PredictionLoop(void *some_void_ptr) else if ((GPS->FlightMode >= fmDescending) && (GPS->FlightMode <= fmLanding)) { // Coming down - try and calculate how well chute is doing - - GPS->CDA = (GPS->CDA*4 + CalculateCDA(Config.payload_weight, - GPS->Altitude/2 + PreviousAltitude/2, - ((double)PreviousAltitude - (double)GPS->Altitude) / POLL_PERIOD)) / 5; - - } - - // Estimate landing position - Altitude = GPS->Altitude; - Latitude = GPS->Latitude; - Longitude = GPS->Longitude; - TimeTillLanding = 0; - - Slot = GetSlot(Altitude); - DistanceInSlot = Altitude + 1 - (Slot * SLOTSIZE); - - while (Altitude > Config.TargetAltitude) - { - Slot = GetSlot(Altitude); - if (Slot == GetSlot(Config.TargetAltitude)) + if (GPS->Altitude < PreviousAltitude) { - DistanceInSlot = Altitude - Config.TargetAltitude; + GPS->CDA = (GPS->CDA*4 + CalculateCDA(Config.payload_weight, + GPS->Altitude/2 + PreviousAltitude/2, + ((double)PreviousAltitude - (double)GPS->Altitude) / POLL_PERIOD)) / 5; } - - DescentRate = CalculateDescentRate(Config.payload_weight, GPS->CDA, Altitude); - TimeInSlot = DistanceInSlot / DescentRate; - - Latitude += Positions[Slot].LatitudeDelta * TimeInSlot; - Longitude += Positions[Slot].LongitudeDelta * TimeInSlot; - - // printf("SLOT %d: alt %lu, lat=%lf, long=%lf, rate=%lf, dist=%lu, time=%lf\n", Slot, Altitude, Latitude, Longitude, DescentRate, DistanceInSlot, TimeInSlot); - - TimeTillLanding = TimeTillLanding + TimeInSlot; - Altitude -= DistanceInSlot; - DistanceInSlot = SLOTSIZE; + } - - GPS->PredictedLatitude = Latitude; - GPS->PredictedLongitude = Longitude; + + + // Estimate landing position + GPS->TimeTillLanding = CalculateLandingPosition(GPS, GPS->Latitude, GPS->Longitude, GPS->Altitude, &(GPS->PredictedLatitude), &(GPS->PredictedLongitude)); + GPS->PredictedLandingSpeed = CalculateDescentRate(Config.payload_weight, GPS->CDA, Config.TargetAltitude); - GPS->TimeTillLanding = TimeTillLanding; - + printf("Expected Descent Rate = %4.1lf (now) %3.1lf (landing), time till landing %d\n", CalculateDescentRate(Config.payload_weight, GPS->CDA, GPS->Altitude), GPS->PredictedLandingSpeed, @@ -176,7 +147,7 @@ void *PredictionLoop(void *some_void_ptr) # endif sprintf(Temp, "%" PRId32 ", %f, %f, %f, %f, %lf\n", GPS->Altitude, GPS->Latitude, GPS->Longitude, GPS->PredictedLatitude, GPS->PredictedLongitude, GPS->CDA); - WriteLog("prediction.txt", Temp); + WritePredictionLog(Temp); } PreviousLatitude = GPS->Latitude; @@ -188,3 +159,42 @@ void *PredictionLoop(void *some_void_ptr) return 0; } + +int CalculateLandingPosition(struct TGPS *GPS, double Latitude, double Longitude, int32_t Altitude, double *PredictedLatitude, double *PredictedLongitude) +{ + double TimeTillLanding, TimeInSlot, DescentRate; + int Slot; + int32_t DistanceInSlot; + + TimeTillLanding = 0; + + Slot = GetSlot(Altitude); + DistanceInSlot = Altitude + 1 - (Slot * SLOTSIZE); + + while (Altitude > Config.TargetAltitude) + { + Slot = GetSlot(Altitude); + + if (Slot == GetSlot(Config.TargetAltitude)) + { + DistanceInSlot = Altitude - Config.TargetAltitude; + } + + DescentRate = CalculateDescentRate(Config.payload_weight, GPS->CDA, Altitude); + TimeInSlot = DistanceInSlot / DescentRate; + + Latitude += Positions[Slot].LatitudeDelta * TimeInSlot; + Longitude += Positions[Slot].LongitudeDelta * TimeInSlot; + + // printf("SLOT %d: alt %lu, lat=%lf, long=%lf, rate=%lf, dist=%lu, time=%lf\n", Slot, Altitude, Latitude, Longitude, DescentRate, DistanceInSlot, TimeInSlot); + + TimeTillLanding = TimeTillLanding + TimeInSlot; + Altitude -= DistanceInSlot; + DistanceInSlot = SLOTSIZE; + } + + *PredictedLatitude = Latitude; + *PredictedLongitude = Longitude; + + return TimeTillLanding; +} diff --git a/tracker/prediction.h b/tracker/prediction.h index ce6076b..b43fdad 100755 --- a/tracker/prediction.h +++ b/tracker/prediction.h @@ -1,6 +1,6 @@ #define DEG2RAD (3.142 / 180) #define SLOTSIZE 100 -#define SLOTS (45000 / SLOTSIZE) +#define SLOTS (60000 / SLOTSIZE) #define POLL_PERIOD 5 struct TPosition @@ -11,3 +11,4 @@ struct TPosition void *PredictionLoop(void *some_void_ptr); int GetSlot(int32_t Altitude); +int CalculateLandingPosition(struct TGPS *GPS, double Latitude, double Longitude, int32_t Altitude, double *PredictedLatitude, double *PredictedLongitude); diff --git a/tracker/process_image b/tracker/process_image new file mode 100755 index 0000000..3cf7179 --- /dev/null +++ b/tracker/process_image @@ -0,0 +1,60 @@ +#!/bin/bash +echo Processing image $2 for channel $1 +# +# This script gets telemetry from the jpeg file comment field, and overlays image with altitude etc. +# +rm -f ssdv.jpg +TELEMETRY="$(exiv2 -pc $2)" +#echo $TELEMETRY +LIST=$(echo $TELEMETRY | tr ";" " ") +#echo $LIST +for ITEM in ${LIST} +do + VALUE=($(echo $ITEM | tr "=" " ")) + if [ "${VALUE[0]}" = "Alt" ]; then + ALTITUDE=${VALUE[1]} + elif [ "${VALUE[0]}" = "MaxAlt" ]; then + MAX_ALT=${VALUE[1]} + elif [ "${VALUE[0]}" = "Lat" ]; then + LATITUDE=${VALUE[1]} + elif [ "${VALUE[0]}" = "Long" ]; then + LONGITUDE=${VALUE[1]} + elif [ "${VALUE[0]}" = "UTC" ]; then + UTC=${VALUE[1]} + elif [ "${VALUE[0]}" = "Ascent" ]; then + ASCENT=${VALUE[1]} + elif [ "${VALUE[0]}" = "Mode" ]; then + MODE=${VALUE[1]} + fi +done +let OFFSET=760-$ALTITUDE*705/44000 +if [ $MODE -eq 0 ]; then + ICON="payload.png" +elif [ $MODE -eq 1 ]; then + ICON="balloon.png" +elif [ $MODE -eq 2 ]; then + ICON="parachute.png" +else + ICON="" +fi +LABEL1="${ALTITUDE}m (${MAX_ALT}m)" +LABEL2=${LATITUDE},${LONGITUDE} +LABEL3=$UTC +convert $2 -gamma 0.8 -font Dosis-Medium -pointsize 48 \ + \ + -gravity SouthWest -strokewidth 2 \ + -stroke '#000C' -annotate 0 " $LABEL1" \ + -stroke none -fill white -annotate 0 " $LABEL1" \ + \ + -gravity South \ + -stroke '#000C' -annotate 0 $LABEL2 \ + -stroke none -fill white -annotate 0 $LABEL2 \ + \ + -gravity SouthEast \ + -stroke '#000C' -annotate 0 "$LABEL3 " \ + -stroke none -fill white -annotate 0 "$LABEL3 " \ + \ + -fill "rgba( 255, 255, 255, 0.25 )" -draw "rectangle 46,30 116,890 " \ + -fill gray -draw "rectangle 81,50 83,870" \ + \ + $ICON -gravity NorthWest -geometry +49+$OFFSET -composite ssdv.jpg diff --git a/tracker/snapper.c b/tracker/snapper.c index fb61229..32c52d9 100755 --- a/tracker/snapper.c +++ b/tracker/snapper.c @@ -1,3 +1,5 @@ +#define _GNU_SOURCE + #include #include #include @@ -17,6 +19,9 @@ #include "gps.h" #include "misc.h" +#pragma GCC diagnostic ignored "-Wformat-truncation" + + int SSDVPacketsToSend(int Channel) { int i, j, Count; @@ -85,11 +90,11 @@ void FindBestImageAndRequestConversion(int Channel, int width, int height) { while ((ep = readdir (dp)) != NULL) { - if (strstr(ep->d_name, ".jpg") != NULL) + if (strcasestr(ep->d_name, ".JPG") != NULL) { if (strchr(ep->d_name, '~') == NULL) { - sprintf(FileName, "%s/%s", SSDVFolder, ep->d_name); + snprintf(FileName, sizeof(FileName), "%.60s/%.30s", SSDVFolder, ep->d_name); stat(FileName, &st); if (st.st_size > LargestFileSize) { @@ -112,32 +117,47 @@ void FindBestImageAndRequestConversion(int Channel, int width, int height) if ((fp = fopen(Config.Channels[Channel].convert_file, "wt")) != NULL) { Config.Channels[Channel].SSDVFileNumber++; - // Config.Channels[Channel].SSDVFileNumber = Config.Channels[Channel].SSDVFileNumber & 255; + Config.Channels[Channel].SSDVFileNumber = Config.Channels[Channel].SSDVFileNumber & 255; - sprintf(Config.Channels[Channel].ssdv_filename, "ssdv_%d_%d.bin", Channel, Config.Channels[Channel].SSDVFileNumber); + snprintf(Config.Channels[Channel].ssdv_filename, sizeof(Config.Channels[Channel].ssdv_filename), "ssdv_%d_%d.bin", Channel, Config.Channels[Channel].SSDVFileNumber); - // External script for ImageMagick etc. - fprintf(fp, "rm -f ssdv.jpg\n"); - fprintf(fp, "if [ -e process_image ]\n"); - fprintf(fp, "then\n"); - fprintf(fp, " ./process_image %d %s %d %d\n", Channel, LargestFileName, width, height); - fprintf(fp, "else\n"); - // Just copy file, unless we're using gphoto2 in which case we need to resize, meaning imagemagick *must* be installed - if (Config.Camera == 3) + if (Config.Camera == 4) { - // resize - fprintf(fp, " convert %s -resize %dx%d ssdv.jpg\n", LargestFileName, width, height); + // Just write parameters to file, and leave it to the external script to do the rest + fprintf(fp, "%s\n%.6s\n%d\n%s\n%d\n%d\n%s\n", Config.Channels[Channel].PayloadID, Config.SSDVSettings, Config.Channels[Channel].SSDVFileNumber, LargestFileName, width, height, Config.Channels[Channel].ssdv_filename); } else { - fprintf(fp, " cp %s ssdv.jpg\n", LargestFileName); + // External script for ImageMagick etc. + fprintf(fp, "rm -f ssdv.jpg\n"); + fprintf(fp, "if [ -e process_image ]\n"); + fprintf(fp, "then\n"); + fprintf(fp, " ./process_image %d %s %d %d\n", Channel, LargestFileName, width, height); + fprintf(fp, "else\n"); + // Just copy file, unless we're using gphoto2 in which case we need to resize, meaning imagemagick *must* be installed + if (Config.Camera == 3) + { + // resize + fprintf(fp, " convert %s -resize %dx%d ssdv.jpg\n", LargestFileName, width, height); + } + else + { + fprintf(fp, " cp %s ssdv.jpg\n", LargestFileName); + } + fprintf(fp, "fi\n"); + + fprintf(fp, "ssdv %s -e -c %.6s -i %d %s %s\n", Config.SSDVSettings, Config.Channels[Channel].PayloadID, Config.Channels[Channel].SSDVFileNumber, "ssdv.jpg", Config.Channels[Channel].ssdv_filename); + fprintf(fp, "mkdir -p %s/$1\n", SSDVFolder); + if (Config.Camera == 5) + { + fprintf(fp, "mv %s/*.jpg %s/$1\n", SSDVFolder, SSDVFolder); + } + else + { + fprintf(fp, "mv %s/*.JPG %s/$1\n", SSDVFolder, SSDVFolder); + } + fprintf(fp, "echo DONE > %s\n", Config.Channels[Channel].ssdv_done); } - fprintf(fp, "fi\n"); - - fprintf(fp, "ssdv %s -e -c %.6s -i %d %s %s\n", Config.SSDVSettings, Config.Channels[Channel].PayloadID, Config.Channels[Channel].SSDVFileNumber, "ssdv.jpg", Config.Channels[Channel].ssdv_filename); - fprintf(fp, "mkdir -p %s/$1\n", SSDVFolder); - fprintf(fp, "mv %s/*.jpg %s/$1\n", SSDVFolder, SSDVFolder); - fprintf(fp, "echo DONE > %s\n", Config.Channels[Channel].ssdv_done); fclose(fp); chmod(Config.Channels[Channel].convert_file, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH); } @@ -187,9 +207,9 @@ void *CameraLoop(void *some_void_ptr) { if (Config.Channels[Channel].Enabled && (Config.Channels[Channel].ImagePackets > 0)) { - // Channel using SSDV + // Channel using images - if (++Config.Channels[Channel].TimeSinceLastImage >= Config.Channels[Channel].ImagePeriod) + if ((Config.Camera != 5) && (++Config.Channels[Channel].TimeSinceLastImage >= Config.Channels[Channel].ImagePeriod)) { // Time to take a photo on this channel @@ -197,10 +217,10 @@ void *CameraLoop(void *some_void_ptr) GetWidthAndHeightForChannel(GPS, Channel, &width, &height); - if ((width > 0) && (height > 0)) + if ((width >= 0) && (height >= 0)) { // Create name of file - sprintf(filename, "/home/pi/pits/tracker/take_pic_%d", Channel); + snprintf(filename, sizeof(filename), "/home/pi/pits/tracker/take_pic_%d", Channel); // Leave it alone if it exists (this means that the photo has not been taken yet) if (access(filename, F_OK ) == -1) @@ -216,7 +236,7 @@ void *CameraLoop(void *some_void_ptr) // Full size images are saved in dated folder names fprintf(fp, "mkdir -p %s/$2\n", Config.Channels[Channel].SSDVFolder); - sprintf(FileName, "%s/$2/$1.jpg", Config.Channels[Channel].SSDVFolder); + snprintf(FileName, sizeof(FileName), "%s/$2/$1.JPG", Config.Channels[Channel].SSDVFolder); if (Config.Camera == 3) { @@ -233,14 +253,34 @@ void *CameraLoop(void *some_void_ptr) { fprintf(fp, "fswebcam -r %dx%d --no-banner %s\n", width, height, FileName); } + else if (Config.Camera == 6) + + { + if ((width == 0) || (height == 0)) + { + fprintf(fp, "rpicam-still %s -o %s\n", Config.CameraSettings, FileName); + } + else + { + fprintf(fp, "rpicam-still --width %d --height %d %s -o %s\n", width, height, Config.CameraSettings, FileName); + } + } + else { - fprintf(fp, "raspistill -st -w %d -h %d -t 3000 -ex auto -mm matrix %s -o %s\n", width, height, Config.CameraSettings, FileName); + if ((width == 0) || (height == 0)) + { + fprintf(fp, "raspistill -st -t 3000 -ex auto -mm matrix %s -o %s\n", Config.CameraSettings, FileName); + } + else + { + fprintf(fp, "raspistill -st -w %d -h %d -t 3000 -ex auto -mm matrix %s -o %s\n", width, height, Config.CameraSettings, FileName); + } } } else { - sprintf(FileName, "%s/$1.jpg", Config.Channels[Channel].SSDVFolder); + snprintf(FileName, sizeof(FileName), "%s/$1.JPG", Config.Channels[Channel].SSDVFolder); if (Config.Camera == 3) { @@ -251,6 +291,10 @@ void *CameraLoop(void *some_void_ptr) { fprintf(fp, "fswebcam -r %dx%d --no-banner %s\n", width, height, FileName); } + else if (Config.Camera == 6) + { + fprintf(fp, "rpicam-still --width %d --height %d %s -o %s\n", width, height, Config.CameraSettings, FileName); + } else { fprintf(fp, "raspistill -st -w %d -h %d -t 3000 -ex auto -mm matrix %s -o %s\n", width, height, Config.CameraSettings, FileName); diff --git a/tracker/startup b/tracker/startup index 6b1b8a8..a840c82 100755 --- a/tracker/startup +++ b/tracker/startup @@ -1,12 +1,12 @@ #!/bin/bash cd /home/pi/pits/tracker -./send_aprs & -echo none | sudo tee /sys/class/leds/led0/trigger -echo 1 | sudo tee /sys/class/leds/led0/brightness sudo ./camera & -#python3 sensors.py & -#python3 astro_led.py & -#python3 webstuff.py +# sleep 30 +# sudo python3 gopro.py & +# python3 sensors.py & +# python3 astro_led.py & +# python3 webstuff.py +# sudo python3 gopro.py & while : do sudo ./tracker diff --git a/tracker/tracker.c b/tracker/tracker.c index c3b2069..11f5a42 100755 --- a/tracker/tracker.c +++ b/tracker/tracker.c @@ -48,11 +48,15 @@ #include "led.h" #include "bmp085.h" #include "bme280.h" +#include "MS5611.h" #include "aprs.h" #include "lora.h" #include "pipe.h" #include "prediction.h" #include "log.h" +#include "cutdown.h" +#include "pin.h" + #ifdef EXTRAS_PRESENT # include "ex_tracker.h" #endif @@ -66,7 +70,7 @@ struct TConfig Config; // BCM PINS #define NTX2B_ENABLE_BCM 17 -int Records, FileNumber; +// int Records, FileNumber; struct termios options; char *SSDVFolder="/home/pi/pits/tracker/images"; @@ -92,7 +96,7 @@ speed_t BaudToSpeed(int baud) void LoadConfigFile(struct TConfig *Config) { - const char* CameraTypes[4] = {"None", "CSI Pi Camera - raspistill", "USB webcam - fswebcam", "USB camera - gphoto2"}; + const char* CameraTypes[7] = {"None", "CSI Pi Camera - raspistill", "USB webcam - fswebcam", "USB camera - gphoto2", "Python Script", "External Camera", "CSI Pi Camera - rpicam-still"}; FILE *fp; int BaudRate; char *filename = "/boot/pisky.txt"; @@ -109,6 +113,7 @@ void LoadConfigFile(struct TConfig *Config) printf("HDMI/Composite outputs will be disabled\n"); } + ReadBoolean(fp, "Send_Field_List", -1, 0, &(Config->SendFieldList)); ReadBoolean(fp, "Disable_ADC", -1, 0, &(Config->DisableADC)); ReadBoolean(fp, "Disable_RTTY", -1, 0, &(Config->DisableRTTY)); Config->Channels[RTTY_CHANNEL].Enabled = !Config->DisableRTTY; @@ -168,6 +173,12 @@ void LoadConfigFile(struct TConfig *Config) printf("BME280 Enabled\n"); } + ReadBoolean(fp, "enable_ms5611", -1, 0, &(Config->EnableMS5611)); + if (Config->EnableMS5611) + { + printf("MS5611 Enabled\n"); + } + ReadString(fp, "pipe_payload", -1, Config->Channels[PIPE_CHANNEL].PayloadID, sizeof(Config->Channels[PIPE_CHANNEL].PayloadID), 0); if (Config->Channels[PIPE_CHANNEL].PayloadID[0]) { @@ -183,7 +194,7 @@ void LoadConfigFile(struct TConfig *Config) Config->MaxADCVoltage = ReadFloat(fp, "adc_vmax", -1, 0, 18.5); Config->Camera = ReadCameraType(fp, "camera"); - printf ("Camera (%s) %s\n", CameraTypes[Config->Camera], Config->Camera ? "Enabled" : "Disabled"); + printf ("Camera Type %d (%s) %s\n", Config->Camera, CameraTypes[Config->Camera], Config->Camera ? "Enabled" : "Disabled"); if (Config->Camera) { @@ -201,39 +212,50 @@ void LoadConfigFile(struct TConfig *Config) } Config->SSDVHigh = ReadInteger(fp, "high", -1, 0, 2000); - printf ("Image size changes at %dm\n", Config->SSDVHigh); + if (Config->Camera != 5) + { + printf ("Image size changes at %dm\n", Config->SSDVHigh); + } if (!Config->DisableRTTY) { Config->Channels[RTTY_CHANNEL].ImageWidthWhenLow = ReadInteger(fp, "low_width", -1, 0, 320); Config->Channels[RTTY_CHANNEL].ImageHeightWhenLow = ReadInteger(fp, "low_height", -1, 0, 240); - printf ("RTTY Low image size %d x %d pixels\n", Config->Channels[RTTY_CHANNEL].ImageWidthWhenLow, Config->Channels[0].ImageHeightWhenLow); Config->Channels[RTTY_CHANNEL].ImageWidthWhenHigh = ReadInteger(fp, "high_width", -1, 0, 640); Config->Channels[RTTY_CHANNEL].ImageHeightWhenHigh = ReadInteger(fp, "high_height", -1, 0, 480); - printf ("RTTY High image size %d x %d pixels\n", Config->Channels[RTTY_CHANNEL].ImageWidthWhenHigh, Config->Channels[0].ImageHeightWhenHigh); Config->Channels[RTTY_CHANNEL].ImagePackets = ReadInteger(fp, "image_packets", -1, 0, 4); - printf ("RTTY: 1 Telemetry packet every %d image packets\n", Config->Channels[RTTY_CHANNEL].ImagePackets); Config->Channels[RTTY_CHANNEL].ImagePeriod = ReadInteger(fp, "image_period", -1, 0, 60); - printf ("RTTY: %d seconds between photographs\n", Config->Channels[RTTY_CHANNEL].ImagePeriod); + + if (Config->Camera != 5) + { + printf ("RTTY Low image size %d x %d pixels\n", Config->Channels[RTTY_CHANNEL].ImageWidthWhenLow, Config->Channels[0].ImageHeightWhenLow); + printf ("RTTY High image size %d x %d pixels\n", Config->Channels[RTTY_CHANNEL].ImageWidthWhenHigh, Config->Channels[0].ImageHeightWhenHigh); + printf ("RTTY: 1 Telemetry packet every %d image packets\n", Config->Channels[RTTY_CHANNEL].ImagePackets); + printf ("RTTY: %d seconds between photographs\n", Config->Channels[RTTY_CHANNEL].ImagePeriod); + } } // Set up full-size image parameters Config->Channels[FULL_CHANNEL].ImageWidthWhenLow = ReadInteger(fp, "full_low_width", -1, 0, 640); Config->Channels[FULL_CHANNEL].ImageHeightWhenLow = ReadInteger(fp, "full_low_height", -1, 0, 480); - printf ("Full Low image size %d x %d pixels\n", Config->Channels[FULL_CHANNEL].ImageWidthWhenLow, Config->Channels[FULL_CHANNEL].ImageHeightWhenLow); Config->Channels[FULL_CHANNEL].ImageWidthWhenHigh = ReadInteger(fp, "full_high_width", -1, 0, 2592); Config->Channels[FULL_CHANNEL].ImageHeightWhenHigh = ReadInteger(fp, "full_high_height", -1, 0, 1944); - printf ("Full High image size %d x %d pixels\n", Config->Channels[FULL_CHANNEL].ImageWidthWhenHigh, Config->Channels[FULL_CHANNEL].ImageHeightWhenHigh); Config->Channels[FULL_CHANNEL].ImagePeriod = ReadInteger(fp, "full_image_period", -1, 0, 60); - printf ("Full size: %d seconds between photographs\n", Config->Channels[FULL_CHANNEL].ImagePeriod); - + Config->Channels[FULL_CHANNEL].ImagePackets = Config->Channels[FULL_CHANNEL].ImagePeriod > 0; Config->Channels[FULL_CHANNEL].Enabled = Config->Channels[FULL_CHANNEL].ImagePackets; + + if (Config->Camera != 5) + { + printf ("Full Low image size %d x %d pixels\n", Config->Channels[FULL_CHANNEL].ImageWidthWhenLow, Config->Channels[FULL_CHANNEL].ImageHeightWhenLow); + printf ("Full High image size %d x %d pixels\n", Config->Channels[FULL_CHANNEL].ImageWidthWhenHigh, Config->Channels[FULL_CHANNEL].ImageHeightWhenHigh); + printf ("Full size: %d seconds between photographs\n", Config->Channels[FULL_CHANNEL].ImagePeriod); + } } // GPS @@ -252,9 +274,87 @@ void LoadConfigFile(struct TConfig *Config) Config->cd_area = ReadFloat(fp, "cd_area", -1, 0, 0.66); Config->payload_weight = ReadFloat(fp, "payload_weight", -1, 0, 0.66); Config->TargetAltitude = ReadInteger(fp, "Target_Altitude", -1, 0, 0); - Config->LandingAltitude = ReadInteger(fp, "Landing_Altitude", -1, 0, Config->TargetAltitude + 200); ReadString(fp, "prediction_id", -1, Config->PredictionID, sizeof(Config->PredictionID), 0); } + + ReadString(fp, "Uplink_Code", -1, Config->UplinkCode, sizeof(Config->UplinkCode), 0); + if (*Config->UplinkCode) + { + printf("Uplink code is '%s'\n", Config->UplinkCode); + } + + ReadBoolean(fp, "Enable_Cutdown", -1, 0, &(Config->EnableCutdown)); + if (Config->EnableCutdown) + { + printf("Cutdown - enabled\n"); + + Config->MinCutdownAltitude = ReadInteger(fp, "Min_Cutdown_Altitude", -1, 0, 0); + if (Config->MinCutdownAltitude > 0) + { + printf(" - inactive below %" PRId32 " metres\n", Config->MinCutdownAltitude); + } + Config->CutdownAltitude = ReadInteger(fp, "Cutdown_Altitude", -1, 0, 0); + if (Config->CutdownAltitude > 0) + { + printf(" - will trigger at an altitude of %" PRId32 " metres\n", Config->CutdownAltitude); + } + + Config->CutdownLatitude = ReadFloat(fp, "Cutdown_Latitude", -1, 0, 0); + Config->CutdownLongitude = ReadFloat(fp, "Cutdown_Longitude", -1, 0, 0); + + ReadString(fp, "Cutdown_Test", -1, Config->CutdownTest, sizeof(Config->CutdownTest), 0); + if (strcasecmp("N", Config->CutdownTest) == 0) + { + printf(" - will trigger North of latitude %.5f\n", Config->CutdownLatitude); + } + else if (strcasecmp("NE", Config->CutdownTest) == 0) + { + printf(" - will trigger North-East of latitude %.5f, longitude %.5f\n", Config->CutdownLatitude, Config->CutdownLongitude); + } + else if (strcasecmp("E", Config->CutdownTest) == 0) + { + printf(" - will trigger East of latitude %.5f\n", Config->CutdownLongitude); + } + else if (strcasecmp("SE", Config->CutdownTest) == 0) + { + printf(" - will trigger South-East of latitude %.5f, longitude %.5f\n", Config->CutdownLatitude, Config->CutdownLongitude); + } + else if (strcasecmp("S", Config->CutdownTest) == 0) + { + printf(" - will trigger South of latitude %.5f\n", Config->CutdownLatitude); + } + else if (strcasecmp("SW", Config->CutdownTest) == 0) + { + printf(" - will trigger South-West of latitude %.5f, longitude %.5f\n", Config->CutdownLatitude, Config->CutdownLongitude); + } + else if (strcasecmp("W", Config->CutdownTest) == 0) + { + printf(" - will trigger West of latitude %.5f\n", Config->CutdownLongitude); + } + else if (strcasecmp("NW", Config->CutdownTest) == 0) + { + printf(" - will trigger North-West of latitude %.5f, longitude %.5f\n", Config->CutdownLatitude, Config->CutdownLongitude); + } + + Config->CutdownTimeSinceLaunch = ReadInteger(fp, "Cutdown_Time_Since_Launch", -1, 0, 0); + if (Config->CutdownTimeSinceLaunch > 0) + { + printf(" - will trigger after a flight time of %lu minutes\n", Config->CutdownTimeSinceLaunch); + Config->CutdownTimeSinceLaunch *= 60; + } + + ReadBoolean(fp, "Cutdown_Burst", -1, 0, &(Config->CutdownBurst)); + if (Config->CutdownBurst) + { + printf(" - will trigger after balloon burst detected\n"); + } + + Config->CutdownPeriod = ReadInteger(fp, "Cutdown_Period", -1, 0, 5); + printf(" - period (on time) is %d seconds\n", Config->CutdownPeriod); + + Config->CutdownPin = ReadInteger(fp, "Cutdown_Pin", -1, 0, 22); + printf(" - pin is %d\n", Config->CutdownPin); + } # ifdef EXTRAS_PRESENT tracker_load_config(fp, Config); @@ -263,6 +363,17 @@ void LoadConfigFile(struct TConfig *Config) Config->ExternalDataFileName[0] = '\0'; ReadString(fp, "external_data", -1, Config->ExternalDataFileName, sizeof(Config->ExternalDataFileName), 0); + // GPS model + Config->GPSModel = ReadCharacter(fp, "gps_model"); + if (Config->GPSModel == '\0') + { + Config->GPSModel = 'U'; // Default UBlox + } + + // Show GPS NMEA ? + Config->ShowGPS = 0; + ReadBoolean(fp, "show_gps", -1, 0, &(Config->ShowGPS)); + // Serial GPS Config->GPSDevice[0] = '\0'; ReadString(fp, "gps_device", -1, Config->GPSDevice, sizeof(Config->GPSDevice), 0); @@ -287,6 +398,23 @@ void LoadConfigFile(struct TConfig *Config) Config->QuietRTTYDuringLoRaUplink = 0; ReadBoolean(fp, "quiet_rtty_for_uplink", -1, 0, &(Config->QuietRTTYDuringLoRaUplink)); + + Config->BlinkenLight = ReadInteger(fp, "Strobe_Pin", -1, 0, -1); + if (Config->BlinkenLight >= 0) + { + Config->FlashBelow = ReadInteger(fp, "Strobe_Alt", -1, 0, Config->SSDVHigh); + Config->Flashing = 0; + + printf("Enabled PCM Strobe Light on pin %d after descending below %d metres\n", Config->BlinkenLight, Config->FlashBelow); + } + + Config->PiezoPin = ReadInteger(fp, "Piezo_Pin", -1, 0, -1); + if (Config->PiezoPin >= 0) + { + Config->WhistleBelow = ReadInteger(fp, "Piezo_Alt", -1, 0, Config->SSDVHigh); + + printf("Enabled Piezo Buzzer on pin %d after descending below %d metres\n", Config->PiezoPin, Config->WhistleBelow); + } LoadAPRSConfig(fp, Config); @@ -324,12 +452,6 @@ void SetMTX2Frequency(char *FrequencyString) snprintf(_mtx2command,17,"@PRG_%02X%06lX\r",_mtx2int-1, _mtx2fractional); printf("MTX2 command is %s\n", _mtx2command); - if (gpioInitialise() < 0) - { - printf("pigpio initialisation failed.\n"); - return; - } - gpioSetMode(NTX2B_ENABLE_BCM, PI_OUTPUT); gpioWaveAddNew(); @@ -348,7 +470,7 @@ void SetMTX2Frequency(char *FrequencyString) } } - gpioTerminate(); + // gpioTerminate(); } char *SerialPortName(void) @@ -486,14 +608,10 @@ int OpenSerialPort(void) void SendSentence(int fd, char *TxLine) { - // printf("Sending sentence ...\n"); write(fd, TxLine, strlen(TxLine)); // Log now while we're waiting for the serial port, to eliminate or at least reduce downtime whilst logging - if (Config.EnableTelemetryLogging) - { - WriteLog("telemetry.txt", TxLine); - } + WriteTelemetryLog(TxLine); // Wait till those characters get sent tcsetattr(fd, TCSAFLUSH, &options); @@ -621,7 +739,7 @@ int main(void) unsigned char Sentence[200]; struct stat st = {0}; struct TGPS GPS; - pthread_t PredictionThread, LoRaThread, APRSThread, GPSThread, DS18B20Thread, ADCThread, CameraThread, BMP085Thread, BME280Thread, LEDThread, LogThread, PipeThread; + pthread_t PredictionThread, LoRaThread, APRSThread, GPSThread, DS18B20Thread, ADCThread, CameraThread, BMP085Thread, BME280Thread, MS5611Thread, LEDThread, CutdownThread, PinThread, LogThread, PipeThread; if (prog_count("tracker") > 1) { @@ -637,8 +755,8 @@ int main(void) exit(1); } - printf("\n\nRASPBERRY PI-IN-THE-SKY FLIGHT COMPUTER\n"); - printf( "=======================================\n\n"); + printf("\n\nRASPBERRY PI-IN-THE-SKY FLIGHT COMPUTER V1.9.1\n"); + printf( "==============================================\n\n"); Config.BoardType = GetBoardType(&Config.i2cChannel); @@ -714,6 +832,12 @@ int main(void) printf("Cannot initialise WiringPi\n"); exit (1); } + + if (gpioInitialise() < 0) + { + printf("pigpio initialisation failed.\n"); + exit (1); + } // Switch off the radio till it's configured pinMode (NTX2B_ENABLE, OUTPUT); @@ -726,7 +850,8 @@ int main(void) pinMode (UBLOX_ENABLE, OUTPUT); digitalWrite (UBLOX_ENABLE, 0); } - + + if (!Config.DisableRTTY) { if (*Config.Frequency) @@ -753,7 +878,23 @@ int main(void) digitalWrite (NTX2B_ENABLE, 1); } - + + + // Turn strobe light off + if (Config.BlinkenLight >= 0) + { + SetupPWMFrequency(Config.BlinkenLight, 50); + ControlPWMOutput(Config.BlinkenLight, 1000); + } + + // Turn piezo buzzer off + if (Config.PiezoPin >= 0) + { + digitalWrite (Config.PiezoPin, 0); + pinMode (Config.PiezoPin, OUTPUT); + } + + // SSDV Folders sprintf(Config.Channels[0].SSDVFolder, "%s/RTTY", SSDVFolder); *Config.Channels[1].SSDVFolder = '\0'; // No folder for APRS images @@ -793,7 +934,7 @@ int main(void) Config.Channels[i].SSDVPacketNumber = -1; Config.Channels[i].ImageFP = NULL; - } + } } if (pthread_create(&GPSThread, NULL, GPSLoop, &GPS)) @@ -872,6 +1013,18 @@ int main(void) fprintf(stderr, "Error creating LED thread\n"); return 1; } + + if (pthread_create(&CutdownThread, NULL, CutdownLoop, &GPS)) + { + fprintf(stderr, "Error creating cutdown thread\n"); + return 1; + } + + if (pthread_create(&PinThread, NULL, PinLoop, &GPS)) + { + fprintf(stderr, "Error creating pin thread\n"); + return 1; + } if (Config.TelemetryFileUpdate > 0) { @@ -899,7 +1052,16 @@ int main(void) return 1; } } - + + if (Config.EnableMS5611) + { + if (pthread_create(&MS5611Thread, NULL, MS5611Loop, &GPS)) + { + fprintf(stderr, "Error creating MS5611 thread\n"); + return 1; + } + } + if (Config.Channels[PIPE_CHANNEL].PayloadID[0]) { if (pthread_create(&PipeThread, NULL, PipeLoop, &GPS)) @@ -982,4 +1144,6 @@ int main(void) } } } + + gpioTerminate(); } diff --git a/tracker/waitfornetwork b/tracker/waitfornetwork new file mode 100755 index 0000000..f7da8ad --- /dev/null +++ b/tracker/waitfornetwork @@ -0,0 +1,3 @@ +while ! ifconfig | grep -F "10.5.5." > /dev/null; do + sleep 1 +done