Compare commits

...

257 Commits

Author SHA1 Message Date
eried a07683b2a6 Merge branch 'next' 2021-11-24 14:58:11 +01:00
eried e3de466743 Version bump 2021-11-24 14:55:06 +01:00
eried 3903aec071 Update credits 2021-11-24 13:47:47 +01:00
Erwin Ried 2a11b59ab7 Merge pull request #424 from intoxsick/custom-splash-screen-from-sd
Custom splash screen from sd
2021-11-24 12:35:41 +01:00
Erwin Ried 0d1d703ba9 Merge pull request #426 from heurist1/update_pocsag_decoder
Update pocsag decoder
2021-11-24 12:32:33 +01:00
Erwin Ried 84ea106f79 Merge pull request #433 from Brumi-2021/New_feature_Increase_freq_range_to_App_Capture
Increase more BW Options to Capture App
2021-11-24 12:31:47 +01:00
Erwin Ried dc06950a94 Merge pull request #419 from Brumi-2021/Final_correction_GCC_10_Compile_error_#199_with_sharebrained_feedback
Final correction gcc 10 compile error #199 with sharebrained feedback
2021-11-24 12:31:29 +01:00
Erwin Ried cf82a3f439 Update README.md 2021-11-24 12:30:28 +01:00
Erwin Ried 16923717d9 Merge pull request #434 from ArjanOnwezen/make-airlines.db
new airlines_db
2021-11-23 10:38:10 +01:00
ArjanOnwezen 23ec3ff11b Update ui_adsb_rx.cpp 2021-11-23 00:16:03 +01:00
Arjan Onwezen bbd9a865de new airlines_db 2021-11-22 17:13:46 -05:00
Erwin Ried e985efd78b Merge pull request #422 from heurist1/adsb-improve-decode-and-gui
Adsb improve decode and gui
2021-11-22 20:53:29 +01:00
Brumi-2021 14484b550a Increase more BW Options to Capture App
PR to increase the BW Options in the Capture App , beyond the current max, 500khz.  (from 600Khz till 2,75Mhz)  all of them work well into  the screen, but only <=  600Khz BW are correctly saved  into SD Card with full captured data. (Onwards, >600Khz optiions, at the moment ,  the created SD card  file has  decimated data, due - to SD Card writting speed limitations),-  but I feel still quite interesting feature to keep them.
2021-11-21 19:35:31 +01:00
ArjanOnwezen 4fcf2cd724 Update LICENSE.md 2021-11-17 12:27:27 +01:00
Erwin Ried 6aa88fb5f9 Merge pull request #432 from ArjanOnwezen/license-correction
License correction
2021-11-17 12:16:38 +01:00
ArjanOnwezen 8f8af0732f Create LICENSE
added GPL3 license
2021-11-17 12:15:52 +01:00
ArjanOnwezen 4322670dbc license-correction
Renamed license file to prevent GitHub to detect wrong licence (GPL v2 i.s.o. GPL v2 or later)
Add correct license for ChiBIOS (GPL v3).
2021-11-17 10:17:53 +01:00
ArjanOnwezen 602a8e6e7f license-correction 2021-11-17 10:15:36 +01:00
Erwin Ried 535f671284 Merge pull request #429 from heurist1/add_2_button_press_back_aka_topleft
Added a quick way of moving the cursor to the top left (back) location
2021-11-14 20:10:04 +01:00
heurist1 40654aba55 moved extract frame back into proc_pocsag 2021-11-07 09:00:00 +00:00
nick aa9917be20 Update hackrf 2021-11-07 06:46:49 +01:00
heurist1 3bbad90949 Scale new integerised power to a better range 2021-11-06 12:35:37 +00:00
heurist1 a0a1954138 Forgot it was called 128 squared 2021-11-06 10:26:01 +00:00
heurist1 94ec0c9087 Sort scaling in GUI 2021-11-06 10:20:39 +00:00
heurist1 a571e40c59 Changed amp to integer 2021-11-06 10:15:17 +00:00
heurist1 79b24ebe13 Improve output 2021-11-01 22:51:06 +00:00
heurist1 8a4564f6f5 Slow down redraw
Combine improvements and slow down refresh.
2021-11-01 20:37:27 +00:00
heurist1 1a21e06bbe Improved overlap packet detection
Improved overlap detection and reduced the threshold
2021-11-01 19:28:56 +00:00
eried da380261b2 Version bump 2021-11-01 11:49:42 +01:00
eried 790ebdff31 Updating the contributors list 2021-11-01 11:48:14 +01:00
eried cf906684cc Merge branch 'next' 2021-11-01 11:46:06 +01:00
heurist1 0d51e3569f Improved decode of type 17 at expense of others
Reject all non type 17 packets
2021-10-31 13:23:48 +00:00
heurist1 0d7bbac6a1 Added switching message length
Improves detections in test file from 77 to 80
2021-10-30 00:53:29 +01:00
heurist1 b499380448 Keep items with loc above those without
Removed fixsbt and added a new state of signal without position. Positions stay higher than non positions.
2021-10-29 16:59:52 +01:00
Brumi-2021 ab8f93805d Small correction,of previous PR , about New_Feature_GCC_10_Compile_errors_#199 (based on sharebrained indications) 2021-10-25 18:11:31 +02:00
Brumi-2021 f4db4e2b53 Solving Compile error on gcc10 . Keeping same safety protection about the size of the array ,but with slightly different sintax. 2021-10-23 22:17:18 +02:00
heurist1 318720f364 Removed all traces of the parameters on the POGSAG config message
Left in the message for the moment, because there are likely to be parameters needed at some point.
2021-10-22 18:11:50 +01:00
heurist1 6ef24ce71c Removed unused GUI items 2021-10-22 15:41:48 +01:00
heurist1 62f063aef5 Removed unused items from the UI
Removed unused items from the UI
2021-10-21 17:14:08 +01:00
heurist1 110b40ea49 Changed content of table
Changed the columns of the table to be more useful
2021-10-17 10:03:28 +01:00
heurist1 2b045ffca6 Mark where to change 2021-10-15 18:40:02 +01:00
heurist1 be01ca6da1 Significantly improved adsb demod
Change preamble detection, reduce cpu load
2021-10-15 14:26:33 +01:00
heurist1 0fed64636a Store old changes to ADSB
Diff in previous changes to ADSB
2021-10-11 10:14:16 +01:00
heurist1 ab364ca497 POCSAG before reformat
This is the POCSAG code before reformat to put smooth and extract packets in the correct place
2021-10-10 09:15:42 +01:00
heurist1 a49c35088d Added back
Added the ability to use the Up and Left buttons simultaneously to cause the cursor to move to the top left of the screen
2021-10-09 22:46:05 +01:00
Erwin Ried 848dba44d8 Merge pull request #395 from Brumi-2021/New-Feature-Add-Gain_TX-control-to-the-Replay-App
Add Gain_TX control to the Replay App
2021-10-06 01:15:32 +02:00
Erwin Ried 5a21c1c76a Merge pull request #413 from Brumi-2021/New_Feature_Remove_Initial_short_Ant_DC_bias_pulse_when_cold_reset
Update main.cpp
2021-09-27 13:08:52 +02:00
Brumi-2021 b335886f42 Update main.cpp
Remove initial short wrong Ant-DC_BIAS pulse. (250 msegs aprox)
2021-09-25 16:37:30 +02:00
Erwin Ried f12eb55f6b Merge pull request #412 from ArjanOnwezen/fix-backlight-time-out
Fixed #398: Changed datatype to be able to store > 255 second for backlight timer
2021-09-06 06:29:28 +02:00
Arjan Onwezen 6871126100 changed datatype to be able to store > 255 second for backlight timer 2021-09-03 16:46:41 +02:00
Erwin Ried 5b4c3514fb Merge pull request #396 from texasyojimbo/next
Create ISM.TXT
2021-07-31 02:18:20 +02:00
AD5NL af7242f7d1 Create ISM.TXT
Center frequencies for ISM bands. These are probably where we should be transmitting if unlicensed.
2021-07-26 15:39:31 -05:00
Brumi-2021 03eae70555 Add Gain_TX control to the Replay App
Current Replay App , shows in the user menu a UI to select two kind of controls for the RF output level :
1-) LNA GAIN (0..40 ) dB => but it has no TX effect because it is the RX-LNA . GAIN
2-) RF AMP (0 / +14dBm , (that was correct , we have two IC's , RX / TX ) (sw is controlling weill .

Note, although SW Version 1.40 do not leave to control drictly the GAIN TX
, that Replay App , in fact, it was using the inheritated selected GAIN TX from any previous usage of MIC App.

That Pull request alllows now to have the following controls
1-) GAIN TX (0..47 ) dB (now it is OK
2-) RF AMP (0 / +14dBm , (that was correct , we have two IC's , RX / TX ) (sw is controlling weill .

Remakrs : After the change , now we can control the GAIN TX , but not "in the fly" . When we are in the Replay loop , any change of the FREQUENCY or GAIN TX will be ignored , till we play STOP / START the loop again. (but the AMP RF (0 /+14 dBs) it works in the loop withouth any problems (same as before ) .
2021-07-15 18:15:44 +02:00
Erwin Ried 010f06c77c Merge pull request #382 from teixeluis/hotfix-adsb-position
Hotfix to issue #362 - ADS-B RX app aircraft position jitter
2021-06-25 10:13:21 +02:00
teixeluis 2f86fa7e99 Merge branch 'hotfix-adsb-position' of https://github.com/teixeluis/portapack-mayhem into hotfix-adsb-position 2021-06-24 15:16:59 +01:00
teixeluis fe806b7bdc Fixed the mixing of aircraft coordinates in the details view, by
checking if the ICAO address of the frame and the current item
in the details view match. Slight refactor by placing the decimal
to string conversion function into the string_format module.

Added fix in the scope of issue #365

FrequencyStepView field in TransmitterView class

FrequencyStepView field in TransmitterView class

Update ui_transmitter.hpp

Update credits

Fixed left padding of the decimal part of the numbers.
2021-06-24 15:16:19 +01:00
teixeluis 22e8add4dd Fixed left padding of the decimal part of the numbers. 2021-06-24 15:10:06 +01:00
teixeluis 93ce2b04c0 Merge branch 'next' into hotfix-adsb-position 2021-06-24 13:44:48 +01:00
teixeluis b09439afb1 Fixed the mixing of aircraft coordinates in the details view, by
checking if the ICAO address of the frame and the current item
in the details view match. Slight refactor by placing the decimal
to string conversion function into the string_format module.
2021-06-23 23:52:15 +01:00
teixeluis 97349b06ef Fixes to the log and screen rendering of the geo coordinates. 2021-06-23 19:54:34 +01:00
Erwin Ried 8329b66926 Update credits 2021-06-22 12:44:19 +02:00
Erwin Ried 52376b12b2 Merge pull request #359 from XxOinvizioNxX/next
Add FrequencyStepView field to the TransmitterView class
2021-06-22 12:30:59 +02:00
Erwin Ried 95f72991c4 Merge pull request #367 from teixeluis/hotfix-rx-hang-issue-365
Added fix in the scope of issue #365
2021-06-22 12:29:51 +02:00
teixeluis 2ceb49e4f2 Added more precise function to calculate NL (based on cmath lib).
Created constants for some of the values scattered in the code.
2021-06-19 23:41:06 +01:00
Erwin Ried 8afd83a4e1 Merge pull request #381 from teixeluis/feature-radiosondes-beep-371
Radiosonde beep pitch RSSI improvements. Keeping current frequency in battery backed RAM
2021-06-17 21:48:14 +02:00
teixeluis 8aff0bb4d8 Improved tone generator for proper frequency control. Also
features a square wave mode.
Added proportional beep duration based on the RSSI as well.
Now reading the current radiosonde frequency from the
battery backed RAM instead starting with the same frequency
all the time.
2021-06-16 23:23:47 +01:00
Erwin Ried 7fe6a3b04e Merge pull request #379 from ArjanOnwezen/category-length-display-fix
fix save frequency gui glitch
2021-06-15 10:20:31 +02:00
Arjan Onwezen c8a8a69832 fix save frequency gui glitch 2021-06-14 21:58:15 +02:00
Erwin Ried de994d5911 Merge pull request #378 from ArjanOnwezen/adding-radiosonde-txt
Adding radiosonde txt
2021-06-14 21:10:10 +02:00
Arjan Onwezen 935db5b58b Added radiosonde.txt 2021-06-14 18:15:42 +02:00
Arjan Onwezen 81f7875764 Added radiosonde.txt 2021-06-14 18:10:45 +02:00
Erwin Ried 0f511508a3 Merge pull request #376 from teixeluis/feature-radiosondes-beep-371
Feature radiosondes beep - working pitch RSSI
2021-06-14 10:35:48 +02:00
teixeluis a80d91fb1e More improvements to the rssi tone. Added saving of the tuned frequency
to the radio model persistent store.
2021-06-13 23:35:33 +01:00
teixeluis 43e123bafe Used temporarily for experimental purposes, no longer needed. 2021-06-11 00:25:24 +01:00
teixeluis fb0672925e Merge remote-tracking branch 'portapack-mayhem/next' into feature-radiosondes-beep-371 2021-06-11 00:15:56 +01:00
teixeluis c2dc7d1cf5 Slight improvement of the tone generator. 2021-06-11 00:13:56 +01:00
teixeluis edcd780402 Improved the pitch RSSI. Still looking for better approach. 2021-06-10 12:10:24 +01:00
teixeluis c85e6a4d52 Now getting rssi events.Pitch changes still to be improved. 2021-06-09 09:10:01 +01:00
Erwin Ried 414ed9a0ec Merge pull request #374 from teixeluis/feature-radiosondes-beep-371
Feature radiosondes beep 371
2021-06-09 05:16:34 +02:00
teixeluis f8f5963bcf Merge remote-tracking branch 'portapack-mayhem/next' into feature-radiosondes-beep-371 2021-06-08 23:11:01 +01:00
teixeluis 9040e780bc Added beep on radiosonde packet decoding, and volume widget to control its level. 2021-06-08 23:07:37 +01:00
Erwin Ried f1ba8c3dee Merge pull request #361 from ArjanOnwezen/iso-date-time
ISO datetime for Clock, FileManager, Frequency manager, added clock UI options
2021-06-07 23:48:19 +02:00
Erwin Ried 3f1af56dfc Merge pull request #372 from ArjanOnwezen/fix-freq-manager-save-file
fix for freq manager save file
2021-06-07 23:47:00 +02:00
Arjan Onwezen 226c0c2d80 fix for freq manager save file 2021-06-06 01:31:30 +02:00
teixeluis d74e1830f8 Added fix in the scope of issue #365 2021-05-22 20:57:48 +01:00
Arjan Onwezen 6bc2cbeda9 ISO datetime for Clock, FileManager, Frequency manager
Added option to show/hide clock (with/without date)
2021-05-17 23:16:15 +02:00
Frey Hertz e76609a74a Update ui_transmitter.hpp 2021-05-14 23:00:09 +03:00
Frey Hertz 97f3f773fb FrequencyStepView field in TransmitterView class 2021-05-14 22:57:25 +03:00
Frey Hertz bf0b9b9881 FrequencyStepView field in TransmitterView class 2021-05-14 22:49:26 +03:00
Erwin Ried 182059b3c6 Merge pull request #358 from ArjanOnwezen/proc_aprsrc_cpp_warning_fix
proc_aprsrx.cpp compile warning fix
2021-05-12 12:40:47 +02:00
Arjan Onwezen c307e9d5ae proc_aprsrx.cpp compile warning fix 2021-05-12 08:09:04 +02:00
Erwin Ried a090f1fb0d Merge pull request #354 from ArjanOnwezen/ui_aprs_rx_cpp_warning_fix
ui_aprs_rx.cpp compiler warning fix
2021-05-12 02:20:55 +02:00
Erwin Ried 62b789e432 Merge pull request #353 from ArjanOnwezen/dsp_modulate_cpp_warning_fix
dsp_modulelate.cpp compiler warning fix
2021-05-12 02:20:17 +02:00
Erwin Ried 42b33c4526 Merge pull request #355 from ArjanOnwezen/aprs_packet_hpp_warning_fix
aprs_packet.hpp compiler warning fix
2021-05-12 02:19:57 +02:00
Erwin Ried a95c3a3e63 Merge pull request #357 from ArjanOnwezen/ui_adsb_rx_hpp_warning_fix
ui_adsb_rx.hpp compiler warning fix
2021-05-12 02:19:41 +02:00
Arjan Onwezen a3ef7d069e ui_adsb_rx.hpp compiler warning fix 2021-05-11 21:47:55 +02:00
Arjan Onwezen f5c3219035 aprs_packet.hpp compiler warning fix 2021-05-10 21:30:45 +02:00
Arjan Onwezen a2e4a15229 dsp.modulelate.cpp compiler warning fix 2021-05-10 19:31:48 +02:00
Arjan Onwezen 1a8319b86e /havoc/firmware/application/apps/ui_aprs_rx.cpp:51:7: warning: unused variable 'entry_age' [-Wunused-variable]
/havoc/firmware/application/apps/ui_aprs_rx.cpp:345:12: warning: unused variable 'age' [-Wunused-variable]
2021-05-10 16:28:46 +02:00
Erwin Ried 2ab4ed591d Update ui_about_simple.cpp 2021-05-03 04:00:39 +02:00
Erwin Ried 4f74e262d2 Merge pull request #351 from ArjanOnwezen/add-gsm-nl.txt
Added GSM-NL.txt contaning Dutch GSM frequencies
2021-05-03 03:59:38 +02:00
Arjan Onwezen 88ca1234d9 Added GSM-NL.txt contaning Dutch GSM frequencies 2021-05-02 18:54:33 +02:00
eried 433529b4cb Merge branch 'next' 2021-04-22 12:12:45 +02:00
eried f161e85f96 Merge branch 'next' of https://github.com/eried/portapack-mayhem into next 2021-04-22 12:11:52 +02:00
Erwin Ried f1fd9a3db1 Removing the warning
Does not applies anymore with the latest release
2021-04-22 12:11:32 +02:00
eried 85e4a43cbd Version bump 2021-04-22 09:34:52 +02:00
Erwin Ried 5f778c1734 Update ui_about_simple.cpp 2021-04-19 10:04:50 +02:00
Erwin Ried c19bcc17af Merge pull request #341 from fossum/feature/adding-snap-values-to-calls
Added a couple snap values to the calls app.
2021-04-19 10:04:07 +02:00
Eric Fossum d60299af95 Added a couple snap values to the calls app. 2021-04-17 22:20:28 +00:00
eried 57ed3ebb43 Merge branch 'pr/325' into next 2021-04-14 09:30:53 +02:00
eried dfa1a98575 Update hackrf 2021-04-14 09:29:45 +02:00
Erwin Ried e21fbbf234 Merge pull request #329 from aldude999/next
AM/SSB/DSB Microphone Functionality
2021-04-14 09:26:09 +02:00
Erwin Ried 136209dc8d Update ui_about_simple.cpp 2021-04-14 09:25:38 +02:00
Erwin Ried 838451f491 Merge pull request #339 from eried/recognition-of-audio-chip
Recognition of audio chip
2021-04-14 09:24:46 +02:00
Erwin Ried 2da482f393 Merge pull request #336 from RedFox-Fr/next
Updates to somes files in sdcard/FREQMAN
2021-04-14 09:22:07 +02:00
Erwin Ried 4cafce3286 Merge pull request #337 from eried/update-hackrf-submodule
Update hackrf submodule
2021-04-14 09:21:36 +02:00
RedFox-Fr f9685517ea Update somes sdcard/FREQMAN files 2021-04-14 00:54:10 +02:00
RedFox-Fr d020016413 Update somes sdcard/FREQMAN files. 2021-04-14 00:45:13 +02:00
Erwin Ried 4c50401df2 Update ui_about_simple.cpp 2021-04-12 13:29:01 +02:00
Erwin Ried d57edd5e25 Merge pull request #333 from zhang00963/next
Automatic recognition of audio chip patch
2021-04-12 12:47:34 +02:00
eried 594890744b New GSM-FR.TXT file for sdcard/FREQMAN
https: //github.com/eried/portapack-mayhem/issues/334
Co-Authored-By: RedFox-Fr <82333326+RedFox-Fr@users.noreply.github.com>
2021-04-12 12:46:34 +02:00
zhang00963 a0eaa70ff4 Realize the automatic recognition of audio chip, including ak4951en/wm8731/wm8731s,Try to fix the max2837 temperature problem 2021-04-11 02:19:31 +08:00
eried 9402634602 Update hackrf 2021-03-25 14:50:22 +01:00
Erwin Ried 41864f06e7 Delete ui_mictx.cpp.1 2021-03-22 10:10:01 +01:00
Erwin Ried 133bfbf07b Delete ui_mictx.hpp.1 2021-03-22 10:09:41 +01:00
DESKTOP-R56EVJP\Alex f65852ff05 Rebased code from new eried repo commits. Changed to to reflect strijar implementation. Fixed previous issue with old ssb-am-tx ui_mictx code. 2021-03-21 20:21:50 -05:00
Erwin Ried 603b7fb1ab Merge pull request #286 from GullCode/ui_looking_glass_warning_fix
Fixed redefined define, possibly overflowing variable, missing constructors
2021-03-16 14:46:23 +01:00
Erwin Ried 9d437aee6a Merge pull request #278 from GullCode/tv_collector_warning_fix
removed unused variables
2021-03-15 11:45:50 +01:00
Erwin Ried fe98a58a4f Merge pull request #277 from GullCode/adsb_warning_fix
Fix adsb warnings
2021-03-15 11:45:32 +01:00
Erwin Ried ee2ebc8302 Merge pull request #272 from euquiq/RADIOSONDE_METEOMAN_LAT_LON_BUG_FIX
Fix bug on radiosonde Meteoman Lat & lon calculation
2021-03-15 11:44:21 +01:00
East2West f15cf78101 Add APRS Receiving App 2021-03-11 22:27:19 -06:00
Erwin Ried f56e8930dc Update dockerfile 2021-03-11 10:34:10 +01:00
Erwin Ried f98c96e98a Update dockerfile-nogit 2021-03-11 10:33:54 +01:00
Erwin Ried de92faf67b Same as the other dockerfile, using get-pip.py 3.4
https://stackoverflow.com/questions/65896334/python-pip-broken-wiith-sys-stderr-writeferror-exc
2021-02-16 09:50:34 +01:00
Erwin Ried 27f354f841 Latest pip broken
https://stackoverflow.com/questions/65896334/python-pip-broken-wiith-sys-stderr-writeferror-exc
2021-02-16 09:49:48 +01:00
Erwin Ried eca25898f2 Merge pull request #276 from GullCode/chibios_warning_fix
Fix warning: cast between incompatible function types
2021-02-16 09:12:22 +01:00
Erwin Ried aea807e1f4 Merge pull request #280 from GullCode/proc_am_audio_warning_fix
Quoted out unused variable
2021-02-16 09:11:20 +01:00
Erwin Ried 8807ab179b Merge pull request #283 from GullCode/proc_btlerx_warning_fix
Quoted CRC calculus as it's unused, voided message
2021-02-16 09:10:42 +01:00
Erwin Ried 7049373d67 Merge pull request #288 from GullCode/ui_navigation_warning_fix
Added missing brace
2021-02-16 09:10:08 +01:00
Erwin Ried 3c30c127e2 Update ui_navigation.cpp 2021-02-16 09:09:49 +01:00
Erwin Ried b82b0f15d0 Merge pull request #311 from ImDroided/next
Added Pocsag bitrate
2021-02-15 09:52:15 +01:00
ImDroided 62c1e4e028 Added Pocsag bitrate
I added 3200 to the bitrates in pocsag per a user request on Facebook.
2021-02-14 12:52:56 -06:00
GullCode cd8732c24f Direct casting to int instead of new var 2021-02-05 23:08:54 +01:00
Erwin Ried 4f90c5efee Updating contributors list 2021-02-03 22:23:12 +01:00
Erwin Ried e0e57e5af2 Merge pull request #290 from GullCode/ui_tv_warning_fix
removed unused var
2021-02-03 22:20:34 +01:00
Erwin Ried d59b0d44d3 Merge pull request #282 from GullCode/proc_am_tv_warning_fix
Quoted unused vars and voided one
2021-02-03 22:09:06 +01:00
Erwin Ried 5ca7398bd9 Merge pull request #279 from GullCode/proc_capture_warning_fix
fix unused result
2021-02-03 22:08:43 +01:00
Erwin Ried dbb46edcb6 Merge pull request #285 from GullCode/proc_nrfrx_warning_fix
Removed unused and voided a var
2021-02-03 22:07:58 +01:00
Erwin Ried 68e75cc98e Merge pull request #293 from GullCode/ui_scanner_warning_fix
Fixed uninitialized / missing switch cases, unused
2021-02-03 22:07:13 +01:00
Erwin Ried f0ca95ee7e Merge pull request #274 from GullCode/sonde_packet_warning_fix
Moved calibytes and calfrchk to cpp file to avoid unused warning
2021-02-03 22:06:27 +01:00
GullCode 1a48fb8c5a removed a var, added a void, removed a unused initializer 2021-01-28 10:08:57 +01:00
Erwin Ried 1a3a60f6ac Merge pull request #281 from GullCode/proc_audio_tx_warning_fix
Added missing initialisation
2021-01-28 00:23:42 +01:00
Erwin Ried 77a7f48958 Merge pull request #284 from GullCode/proc_pocsag_warning_fix
Added missing initialisations
2021-01-28 00:23:10 +01:00
Erwin Ried 523877e0d1 Merge pull request #287 from GullCode/ais_packet_warning_fix
Added missing default constructor
2021-01-28 00:22:37 +01:00
Erwin Ried 282416169d Merge pull request #289 from GullCode/ertkey_warning_fix
Added missing default constructor
2021-01-28 00:18:31 +01:00
Erwin Ried 8e8e156477 Merge pull request #291 from GullCode/ui_fileman_warning_fix
Added a voided v
2021-01-28 00:16:40 +01:00
Erwin Ried b1135aab0a Merge pull request #292 from GullCode/ui_morse_warning_fix
added a voided var
2021-01-28 00:16:27 +01:00
Erwin Ried 36f9e0cb7c Merge pull request #294 from GullCode/ui_settings_warning_fix
Added missing initializer
2021-01-28 00:16:07 +01:00
Erwin Ried 9bd74f32d0 Merge pull request #295 from GullCode/ui_sonde_warning_fix
Added missing initializer
2021-01-28 00:15:39 +01:00
Erwin Ried dd0042b5f1 Merge pull request #297 from GullCode/soundboard_app_warning_fix
added a voided var
2021-01-28 00:14:55 +01:00
GullCode fb61ad55c0 added a voided var 2021-01-27 17:20:07 +01:00
GullCode ca7b5e110c Added missing initializer 2021-01-27 17:10:37 +01:00
GullCode a6a41ca5a5 Added missing initialize 2021-01-27 17:08:09 +01:00
GullCode 02d69e54eb Fixed uninitialized / missing switch cases, unused 2021-01-27 17:03:20 +01:00
GullCode b75ef345a5 added a voided var 2021-01-27 16:57:42 +01:00
GullCode 8c84719598 Added two voided vars 2021-01-27 16:54:17 +01:00
GullCode 8bfeba0d89 Added a voided v 2021-01-27 16:50:48 +01:00
GullCode c3c680fc04 removed unused var 2021-01-27 16:46:58 +01:00
GullCode f0457c106d Added missing default constructor 2021-01-27 16:43:13 +01:00
GullCode 5d73175a17 Added missing brace 2021-01-27 16:39:50 +01:00
GullCode 45b874694e Added missing default constructor 2021-01-27 16:36:44 +01:00
GullCode 42113434f0 Fixed redefined define, possibly overflowing variable, missing constructors 2021-01-27 16:30:09 +01:00
GullCode e9e4d20302 Removed unused and voided a var 2021-01-27 16:11:20 +01:00
GullCode ec520bf08c Added missing initialisations 2021-01-27 16:07:03 +01:00
GullCode 486c1d6bcd Quoted CRC calculus as it's unused, voided message 2021-01-27 16:02:59 +01:00
GullCode b7eb095dd8 Quoted unused vars and voided one 2021-01-27 15:58:56 +01:00
GullCode 3c14d38286 Added missing initialisation 2021-01-27 15:54:45 +01:00
GullCode 3166a66756 Quoted out unused variable 2021-01-27 15:52:17 +01:00
GullCode 961e115cc6 fix unused result 2021-01-27 15:48:41 +01:00
GullCode 1483160df4 removed unused variables 2021-01-27 15:45:04 +01:00
GullCode c3fe053eb2 Fix adsb warnings 2021-01-27 15:38:05 +01:00
GullCode 6c2950cfe8 Fix warning: cast between incompatible function types from 'void (*)(void *)' to 'msg_t (*)(void *)' 2021-01-27 15:32:46 +01:00
Erwin Ried 89d46d59ee Merge pull request #275 from GullCode/channel_stat_collector_warning_fix
Fix __SIMD32 warning
2021-01-27 15:22:39 +01:00
Erwin Ried 0d7aa1d32c Merge pull request #273 from GullCode/spi_image_warning_fix
missing contructor
2021-01-27 15:17:42 +01:00
GullCode 266e398224 Fix __SIMD32 warning 2021-01-27 15:16:43 +01:00
GullCode e75677d366 Moved calibytes and calfrchk to cpp file to avoid unused warning 2021-01-27 15:09:02 +01:00
GullCode 1c1d3e9897 missing contructor 2021-01-27 14:57:22 +01:00
euquiq 50bab791dd Fix bug on radiosonde Meteoman Lat & lon calculation
The underlying function used for calculating Latitude and Longitude -also used in other places inside the radiosonde app- was returning a positive value always.

But it needs to cope with negative values also (i.e. Lat and Lon)

Fixed by just changing the returning value into int32_t (even if the calculation is done in uint32_t, the actual sign is passed thru when returning the calculated value -those are the same 4 bytes, interpreted either as (before) unsigned or (now) signed)
2021-01-25 23:41:19 -03:00
Erwin Ried 19ebf14e8c Update README.md 2021-01-20 11:03:39 +01:00
eried 8b6442ee79 Merge branch 'next' 2021-01-20 10:53:24 +01:00
eried 456b55d552 Merge branch 'next' of https://github.com/eried/portapack-mayhem into next 2021-01-20 10:52:56 +01:00
Erwin Ried 3dfb23481f Update README.md 2021-01-20 10:29:13 +01:00
eried 000b2d3e7d Version bump 2021-01-20 10:02:52 +01:00
Erwin Ried faa4affba1 Update README.md 2021-01-20 09:42:21 +01:00
Erwin Ried 099afda51c Update README.md 2021-01-20 09:41:25 +01:00
Erwin Ried 5c09efe8f6 Update README.md 2021-01-20 08:33:58 +01:00
Erwin Ried 157f08bc49 Update README.md 2021-01-20 08:32:27 +01:00
Erwin Ried d5e6d9d662 Merge pull request #257 from strijar/waterfall-bar
Waterfall filter bar
2021-01-12 21:57:19 +01:00
Erwin Ried 64bdd3e018 Merge pull request #263 from euquiq/JAMMER-TX_COOLDOWN_TIMERS_IN_SECONDS
JAMMER NOW INCLUDES TWO TIMERS
2021-01-12 21:57:04 +01:00
euquiq 928bce083a Cosmetics + Jammer now in green + Jitter
I think the Jammer deserves a green icon, since it actually does it job pretty well.

Then there is a Jitter parameter. It allows to introduce a jitter from 1/60th of a second up to 60/60th of a second (a full one). It will delay / move forward either the TX or the cooldown period for a maximum of a half of the time you choose as jitter.

Meaning: If I choose 60/60th, a full second of jitter, it will produce a random number from 1 to 60.

Then it will calculate jitter = 30 - randomnumber

THen it will "add" that (positive or negative) time to the  time counter for the next jitter change of state.
2021-01-08 11:54:21 -03:00
euquiq 8859c3d80c JAMMER NOW INCLUDES TWO TIMERS
Each driving the TX and COOLDOWN timers.
2021-01-07 17:51:38 -03:00
Белоусов Олег 1e9406d091 Cleaned up a bit 2020-12-24 11:14:23 +03:00
Белоусов Олег 63f6a885d8 Implemented correct display of the filter indicator on the waterfall 2020-12-23 18:00:51 +03:00
Erwin Ried 608c8c3597 Merge pull request #222 from euquiq/simplify-persistent-memory-ui-config
just making Persistent Memory easier to read
2020-11-23 12:05:07 +01:00
euquiq 6aee1c9c30 just making Persistent Memory easier to read
Some internal code re-writing in order to simplify a bit, and hopefully making it easier to understand what's going on inside there.
2020-11-03 02:14:16 -03:00
Erwin Ried 554db24b1f Merge branch 'next' 2020-11-01 17:27:45 +01:00
Erwin Ried 2fa6df596a New icon and shortcut 2020-11-01 17:26:29 +01:00
Erwin Ried aeefecf5c6 Version bump 2020-10-31 22:00:19 +01:00
Erwin Ried 06d4916144 Merge pull request #187 from rascafr/rascafr/scanner-app-load-frequency-files
Scanner app - Load custom frequency files
2020-10-27 22:03:26 +01:00
Erwin Ried 911f0c8881 Merge pull request #213 from euquiq/Fix-APRS-TX-app
Fix aprs tx app
2020-10-27 21:57:50 +01:00
Erwin Ried 01cfd360fe Merge pull request #215 from euquiq/looking_glass_full_bw_cascade_scanner
New "looking Glass" app
2020-10-27 21:57:11 +01:00
euquiq 13ce56f7bf Added "RESOLUTION" parameter
Discord User jteich did some investigation (Thanks!) and helped me understanding this rather obscure parameter:

Internally, is called "TRIGGER", and is passed into the baseband when configuring the desired spectrum sample rate.

Please forgive me in advance if this explanation is not 100% correct. It's only my interpretation, based on my own observation and jteich's comments over Discord chat.

This trigger parameter apparently determines the amount of data over time used for calculating the signal's power inside each specttrum's bin, before considering it "done".

In short, if you lower this resolution value then the cascade will tend to be rendered a bit faster, while kind of blind to tiny signals.

On the other hand, a bigger value will help rendering and distinguishing different signals on the cascade.

Too big a value can easily clutter up the cascade. But then it may be a "blessing" when inspecting higher freuqencies -where hackrf is more deaf"

The default value of 32 is quite decent. But then, now you can experiment with it. Cheers
2020-10-26 23:43:35 -03:00
euquiq 210ec9dd1b Looking Glass with PRESETS
Added a PRESETS.TXT file (inside /LOOKINGGLASS folder).

Also optimized the way the spectrum signal is integrated into the cascade.

Added provision for ranges lower than 240MHz but I am afraid that at this time it will not be advisable to lower ranges any more than 240MHz, since some artifacts and frequency running - moving out of place- occurs.

I can only hope that someone with a better understanding of hackrf's inner code can fix this issue and perhaps enhance the scanning speed.

I found some "original commenting" inside the code:

	// TODO: Move more low-level radio control stuff to M4. It'll enable tighter
	// synchronization for things like wideband (sweeping) spectrum analysis, and
	// protocols that need quick RX/TX turn-around.

Which makes me think that there are things "missing"  from the portapack side of the code, for allowing serious speed sweeping. So I am concluding that with current "portapack framework" this might be "the best possible thing".

It is to be noted that the "new" internal sweep mode code is signed by:

 * Copyright 2016 Mike Walters, Dominic Spill
 *
 * This file is part of HackRF.

Maybe Mike or Dominic can be contacted and hopefully lend a hand on enhancing this code.
2020-10-26 12:53:10 -03:00
Erwin Ried 47c5c1e9e5 Update generate_world_map.bin.py
Fix for https://github.com/eried/portapack-mayhem/issues/217
2020-10-24 22:38:43 +02:00
Erwin Ried 1207bac215 Ignore python venvs 2020-10-24 22:26:31 +02:00
Erwin Ried 94fcab19d9 Merge pull request #214 from dqs105/clkout_enable
Add options for enabling CLKOUT.
2020-10-24 22:19:39 +02:00
dqs105 7ca322fed4 Added options for tuning CLKOUT freq.
- Now we have variable CLKOUT.
- CLKOUT can be set between 10kHz and 60MHz.
(The output signal will become mostly sine shape when reaching 50MHz.)
- Click on freq setting field to change tuning step.
2020-10-24 00:24:05 +08:00
euquiq 3e1d726064 Enhancement and bug
Added a nicer MARKER (thanks to XSX(H1) contributor for the suggestion)

Fixed a bug that made the screen scroll from top, when using a popup "window" and returning (like, when pressing the DC VOLTAGE enable / disable" button on top bar) THanks to GregoryFenton for the testing and bug spotting!
2020-10-23 12:19:57 -03:00
euquiq 01ba7a57ea New "looking Glass" app
Capable of showing a cascade with full bandwidth scan. You can select Min and Max Mhz for the cascade.

You can move a marker so to (aproximately) know  a particular frequency on the cascade. If you press the select button, the app will jump into the RX -> AUDIO app, already tuned into the just "marked" frequency.

This first version SURELY has space for lots of optimizations and improvement in general.
2020-10-21 13:21:34 -03:00
dqs105 699504a703 Removed trailing spaces. 2020-10-14 20:35:51 +08:00
dqs105 48ccc1e988 Merge branch 'clkout_enable' of https://github.com/dqs105/portapack-mayhem into upstream 2020-10-14 20:15:36 +08:00
euquiq 95f7eda9c5 APRS TX app is working fine now!
Several old bugs squashed.

On the APRS side, most notably, SSID numbers where shifted left twice, instead of once, and bits 5,6 where not properly set.

On AX.25 side, the bit stuffing part of the encoder was not placing the zero bit on the right place.

Finally, I changed APRS icon from ORANGE to GREEN, since even this may be a simple app, now it's doing its work as intended.
2020-10-10 20:24:11 -03:00
Erwin Ried 4306ba5d83 Updated models 2020-10-07 20:44:09 +02:00
eried 530789f963 Create README.md 2020-10-07 10:05:37 +02:00
eried ebe6675a3a Adding files of the printed case 2020-10-07 10:03:04 +02:00
Erwin Ried af3130faa8 Merge pull request #205 from texasyojimbo/next
TIMEFREQ.TXT
2020-10-03 01:15:30 +02:00
AD5NL 3419ece682 Create TIMEFREQ.TXT
HF Time and frequency standard stations
2020-10-02 16:47:04 -05:00
euquiq b22448de75 Fixes SSID value error on APX TX
Values where left bit-shifted upon being entered by the user, so resulting SSID being transmitted was a different number.  This shifting was happening both on Source and Destination SSID values.
2020-09-28 17:54:27 -03:00
Erwin Ried 98ba332cda Version bump 2020-09-25 03:34:51 +02:00
Erwin Ried f3503e6844 Fix for the freezing when no on_touch_release function 2020-09-25 03:34:05 +02:00
Erwin Ried 883a62238d Version bump 2020-09-24 22:04:59 +02:00
Erwin Ried 9021e43041 Create FUNDING.yml 2020-09-24 11:06:30 +02:00
Erwin Ried e75dfdaf2d Merge pull request #196 from strijar/morse_tx
Bug fix
2020-09-24 10:33:06 +02:00
Белоусов Олег cafc4fdec4 Bug fix 2020-09-24 11:04:00 +03:00
Erwin Ried 99af859e13 Default null for on touch release and press
Fixes https://github.com/eried/portapack-mayhem/issues/194
2020-09-24 03:15:40 +02:00
Erwin Ried 4f2b306784 Merge pull request #195 from strijar/morse_tx
Morse TX - speed in WPS and repeat loop
2020-09-22 21:54:51 +02:00
Белоусов Олег 25ba2f1391 Morse TX loops 2020-09-22 16:12:46 +03:00
Белоусов Олег a5a3ba184a Morse TX speed in WPS 2020-09-22 11:46:44 +03:00
Erwin Ried 17cb092978 Merge pull request #191 from dqs105/siggen_fix
SigGen "Stop after" freeze fix & stronger CW signal
2020-09-20 15:08:26 +02:00
Erwin Ried 1ff027ed01 Merge pull request #192 from RobertoD91/next
Add VHF marine frequence on SD
2020-09-20 15:06:22 +02:00
dqs105 e89b7683ef Fixed "Stop After" freeze & stronger CW signal 2020-09-19 14:24:40 +08:00
dqs105 1029322405 Fix "Stop After" Freeze. 2020-09-19 14:22:28 +08:00
Roberto d280054093 Add VHF marine frequence on SD 2020-09-19 00:10:53 +02:00
dqs105 66a841e079 combine clkout_config => ui_config 2020-09-17 12:47:34 +08:00
dqs105 b15b781039 Added options for enabling CLKOUT.
- CLKOUT can be enabled in Radio settings and status bar.
- Fixed a typo(I believe) in ui_navigation.
2020-09-16 19:27:56 +08:00
Erwin Ried 95be75a8af Merge pull request #172 from dqs105/new_splash
New custom splash.
2020-09-14 22:53:59 +02:00
Rascafr f95467d71b loaded_file_name goes back to default 'scanner' file if load step fails 2020-09-11 22:17:40 +02:00
Rascafr 200c57f990 Save new frequencies into the default / user loaded freqman txt file 2020-09-11 22:15:35 +02:00
Rascafr d2c66f4515 Removed confirmation modal after load is successful 2020-09-11 22:07:12 +02:00
Rascafr dbdb0bd379 Added ui_scanner load button for any .txt frequency file 2020-09-11 21:11:42 +02:00
dqs105 5cbbf62c5f Removed drawRAW.
- BMP files can be loaded directly now, so it can be removed.
2020-08-29 10:53:26 +08:00
dqs105 80f074b82f Added BMP file support.
- Now can load splash.bmp!
2020-08-28 12:23:52 +08:00
dqs105 7116ca3f91 Fixed typo. 2020-08-28 00:27:52 +08:00
dqs105 8463cba4b6 Splash generator.
(Modified from the one generates world_map.bin)
2020-08-28 00:22:36 +08:00
dqs105 78f2e417d6 Fixed RGB565 mode & clean up 2020-08-28 00:21:09 +08:00
dqs105 ac12bf5f50 modify file extension from data to bin. 2020-08-27 23:16:05 +08:00
dqs105 8edaa1e51e Added new custom splash.
- Custom splash now can be loaded from SD card.
2020-08-27 23:07:48 +08:00
169 changed files with 15107 additions and 3375 deletions
+12
View File
@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: https://salt.bountysource.com/checkout/amount?team=portapack-mayhem
+7 -1
View File
@@ -63,4 +63,10 @@ CMakeFiles/
# Host OS leftovers
.DS_Store
/firmware/CMakeCache.txt
/firmware/CMakeCache.txt
# Python env
env/
# Other
*.bak
+587 -252
View File
@@ -1,281 +1,622 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
TERMS AND CONDITIONS
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
0. Definitions.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
"This License" refers to version 3 of the GNU General Public License.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
A "covered work" means either the unmodified Program or a work based
on the Program.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
1. Source Code.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
The Corresponding Source for a work in source code form is that
same work.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
2. Basic Permissions.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
13. Use with the GNU Affero General Public License.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
14. Revised Versions of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
NO WARRANTY
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
15. Disclaimer of Warranty.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
@@ -287,15 +628,15 @@ free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
{description}
Copyright (C) {year} {fullname}
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
@@ -303,37 +644,31 @@ the "copyright" line and a pointer to where the full notice is found.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
{signature of Ty Coon}, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
+339
View File
@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
{description}
Copyright (C) {year} {fullname}
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
{signature of Ty Coon}, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
+7
View File
@@ -0,0 +1,7 @@
# License
Portapack Mayhem is distributed under GPL v3.0, however some sub projects might have different (GPL v3.0 compatible) licenses.
## Other Licenses (compatible with GPL v3.0)
- Most of Portapack Mayhem is distributed under GPL-2.0-or-later. [license](LICENSE.GPL-2.0-or-later)
- ChibiOS is distributed under GPL v3.0. [license](/firmware/chibios/license.txt)
- Portapack-ChibiOS is distributed under Apache-2.0 [license](http://www.apache.org/licenses/LICENSE-2.0)
+3 -3
View File
@@ -1,6 +1,6 @@
# PortaPack Mayhem
[![Build Status](https://travis-ci.com/eried/portapack-mayhem.svg?branch=master)](https://travis-ci.com/eried/portapack-mayhem) [![buddy pipeline](https://app.buddy.works/eried/portapack/pipelines/pipeline/252276/badge.svg?token=48cd59d53de0589a8fbe26bc751d77a59a011cf72581da049343879402991c34 "buddy pipeline")](https://app.buddy.works/eried/portapack/pipelines/pipeline/252276) [![CodeScene Code Health](https://codescene.io/projects/8381/status-badges/code-health)](https://codescene.io/projects/8381) [![GitHub All Releases](https://img.shields.io/github/downloads/eried/portapack-mayhem/total)](https://github.com/eried/portapack-mayhem/releases) [![GitHub Releases](https://img.shields.io/github/downloads/eried/portapack-mayhem/latest/total)](https://github.com/eried/portapack-mayhem/releases/latest) [![Docker Hub Pulls](https://img.shields.io/docker/pulls/eried/portapack.svg)](https://hub.docker.com/r/eried/portapack) [![Discord Chat](https://img.shields.io/discord/719669764804444213.svg)](https://discord.gg/tuwVMv3) [![Check bounties!](https://img.shields.io/bountysource/team/portapack-mayhem/activity?color=%2333ccff&label=bountysource%20%28USD%29&style=plastic)](https://www.bountysource.com/teams/portapack-mayhem/issues)
[![Build Status](https://travis-ci.com/eried/portapack-mayhem.svg?branch=master)](https://travis-ci.com/eried/portapack-mayhem) [![CodeScene Code Health](https://codescene.io/projects/8381/status-badges/code-health)](https://codescene.io/projects/8381) [![GitHub All Releases](https://img.shields.io/github/downloads/eried/portapack-mayhem/total)](https://github.com/eried/portapack-mayhem/releases) [![GitHub Releases](https://img.shields.io/github/downloads/eried/portapack-mayhem/latest/total)](https://github.com/eried/portapack-mayhem/releases/latest) [![Docker Hub Pulls](https://img.shields.io/docker/pulls/eried/portapack.svg)](https://hub.docker.com/r/eried/portapack) [![Discord Chat](https://img.shields.io/discord/719669764804444213.svg)](https://discord.gg/tuwVMv3) [![Check bounties!](https://img.shields.io/bountysource/team/portapack-mayhem/activity?color=%2333ccff&label=bountysource%20%28USD%29&style=plastic)](https://www.bountysource.com/teams/portapack-mayhem/issues)
This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmware, which itself was a fork of the [PortaPack](https://github.com/sharebrained/portapack-hackrf) firmware, an add-on for the [HackRF](http://greatscottgadgets.com/hackrf/). A fork is a derivate, in this case one that has extra features and fixes when compared to the older versions.
@@ -26,7 +26,7 @@ To support the people behind the hardware, please buy a genuine [HackRF](https:/
## Where is the latest firmware?
The current stable release is on the [![GitHub release (latest by date)](https://img.shields.io/github/v/release/eried/portapack-mayhem?label=Releases&style=social)](https://github.com/eried/portapack-mayhem/releases/latest) page. Follow the instructions you can find in the release description. There is also [nightly builds](https://github.com/eried/portapack-mayhem/releases/tag/nightly) generated periodically, which include the latest commits, but they may contain incomplete or buggy functionality.
The current stable release is on the [![GitHub release (latest by date)](https://img.shields.io/github/v/release/eried/portapack-mayhem?label=Releases&style=social)](https://github.com/eried/portapack-mayhem/releases/latest) page. Follow the instructions you can find in the release description.
## Is this the newest firmware for my PortaPack?
Most probably: **YES**. *If you find new features somewhere else, please [suggest](https://github.com/eried/portapack-mayhem/issues/new/choose) them*.
@@ -39,7 +39,7 @@ This fork (**Mayhem**) uses *major.minor.release* [semantic versioning](https://
## What about Havoc/GridRF/jamesshao8/jboone's?
* jboone's PortaPack: the [vanilla](https://en.wikipedia.org/wiki/Vanilla_software) experience
* Havoc: It was the most popular fork of jboone's PortaPack, currrently, it is not being maintained nor updated
* jamesshao8: He keeps his own version of the fork, while not attached as a fork to anything
* jamesshao8: He keeps his own version of the fork, while not attached as a fork to anything. Latest functions do not follow the license and are not being published with source code, so keep this in mind
* GridRF: They sell PortaPack clones with their own firmware based on a old version, which has no sourcecode available
## How can I collaborate
+1 -1
View File
@@ -18,7 +18,7 @@ RUN apt-get update && \
apt-get -qy autoremove
#Install current pip from PyPa
RUN curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py && \
RUN curl https://bootstrap.pypa.io/pip/3.4/get-pip.py -o get-pip.py && \
python get-pip.py
#Fetch additional dependencies from Python 2.x pip
+2 -2
View File
@@ -15,7 +15,7 @@ RUN apt-get update && \
apt-get -qy autoremove
#Install current pip from PyPa
RUN curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py && \
RUN curl https://bootstrap.pypa.io/pip/3.4/get-pip.py -o get-pip.py && \
python3 get-pip.py
#Fetch additional dependencies from Python 3.x pip
@@ -38,4 +38,4 @@ RUN mkdir ~/bin && cd ~/bin && \
for tool in gcc g++ cpp c++;do ln -s $(which ccache) arm-none-eabi-$tool;done
CMD cd .. && cd build && \
cmake .. && make firmware
cmake .. && make firmware
+3
View File
@@ -140,6 +140,7 @@ set(CPPSRC
${COMMON}/png_writer.cpp
${COMMON}/pocsag.cpp
${COMMON}/pocsag_packet.cpp
${COMMON}/aprs_packet.cpp
${COMMON}/portapack_io.cpp
${COMMON}/portapack_persistent_memory.cpp
${COMMON}/portapack_shared_memory.cpp
@@ -218,6 +219,7 @@ set(CPPSRC
apps/ui_adsb_rx.cpp
apps/ui_adsb_tx.cpp
apps/ui_afsk_rx.cpp
apps/ui_aprs_rx.cpp
apps/ui_btle_rx.cpp
apps/ui_nrf_rx.cpp
apps/ui_aprs_tx.cpp
@@ -231,6 +233,7 @@ set(CPPSRC
apps/ui_keyfob.cpp
apps/ui_lcr.cpp
apps/lge_app.cpp
apps/ui_looking_glass_app.cpp
apps/ui_mictx.cpp
apps/ui_modemsetup.cpp
apps/ui_morse.cpp
+2 -1
View File
@@ -199,7 +199,7 @@ void AnalogTvView::on_show_options_rf_gain() {
void AnalogTvView::on_show_options_modulation() {
std::unique_ptr<Widget> widget;
const auto modulation = static_cast<ReceiverModel::Mode>(receiver_model.modulation());
static_cast<ReceiverModel::Mode>(receiver_model.modulation());
tv.show_audio_spectrum_view(true);
set_options_widget(std::move(widget));
@@ -216,6 +216,7 @@ void AnalogTvView::on_reference_ppm_correction_changed(int32_t v) {
}
void AnalogTvView::on_headphone_volume_changed(int32_t v) {
(void)v; //avoid warning
//tv::TVView::set_headphone_volume(this,v);
}
+1 -1
View File
@@ -107,7 +107,7 @@ private:
std::unique_ptr<Widget> options_widget { };
tv::TVWidget tv { true };
tv::TVWidget tv { };
void on_tuning_frequency_changed(rf::Frequency f);
void on_baseband_bandwidth_changed(uint32_t bandwidth_hz);
+44 -3
View File
@@ -74,17 +74,58 @@ CaptureAppView::CaptureAppView(NavigationView& nav) {
option_bandwidth.on_change = [this](size_t, uint32_t base_rate) {
sampling_rate = 8 * base_rate; // Decimation by 8 done on baseband side
/* base_rate is used for FFT calculation and display LCD, and also in recording writing SD Card rate. */
/* ex. sampling_rate values, 4Mhz, when recording 500 khz (BW) and fs 8 Mhz , when selected 1 Mhz BW ...*/
/* ex. recording 500khz BW to .C16 file, base_rate clock 500khz x2(I,Q) x 2 bytes (int signed) =2MB/sec rate SD Card */
waterfall.on_hide();
record_view.set_sampling_rate(sampling_rate);
receiver_model.set_sampling_rate(sampling_rate);
/* Set up proper anti aliasing BPF bandwith in MAX2837 before ADC sampling according to the new added BW Options . */
switch(sampling_rate) { // we use the var fs (sampling_rate) , to set up BPF aprox < fs_max/2 by Nyquist theorem.
case 0 ... 2000000: // BW Captured range (0 <= 250Khz max ) fs = 8 x 250 Khz
anti_alias_baseband_bandwidth_filter = 1750000; // Minimum BPF MAX2837 for all those lower BW options.
break;
case 4000000 ... 6000000: // BW capture range (500k ... 750Khz max ) fs_max = 8 x 750Khz = 6Mhz
// BW 500k ... 750khz , ex. 500khz (fs = 8*BW = 4Mhz) , BW 600Khz (fs = 4,8Mhz) , BW 750 Khz (fs = 6Mhz)
anti_alias_baseband_bandwidth_filter = 2500000; // in some IC MAX2837 appear 2250000 , but both works similar.
break;
case 8800000: // BW capture 1,1Mhz fs = 8 x 1,1Mhz = 8,8Mhz . (1Mhz showed slightly higher noise background).
anti_alias_baseband_bandwidth_filter = 3500000;
break;
case 14000000: // BW capture 1,75Mhz , fs = 8 x 1,75Mhz = 14Mhz
// good BPF ,good matching, but LCD making flicker , refresh rate should be < 20 Hz , but reasonable picture
anti_alias_baseband_bandwidth_filter = 5000000;
break;
case 16000000: // BW capture 2Mhz , fs = 8 x 2Mhz = 16Mhz
// good BPF ,good matching, but LCD making flicker , refresh rate should be < 20 Hz , but reasonable picture
anti_alias_baseband_bandwidth_filter = 6000000;
break;
case 20000000: // BW capture 2,5Mhz , fs= 8 x 2,5 Mhz = 20Mhz
// good BPF ,good matching, but LCD making flicker , refresh rate should be < 20 Hz , but reasonable picture
anti_alias_baseband_bandwidth_filter = 7000000;
break;
default: // BW capture 2,75Mhz, fs = 8 x 2,75Mhz= 22Mhz max ADC sampling) and others.
// We tested also 9Mhz FPB stightly too much noise floor , better 8Mhz
anti_alias_baseband_bandwidth_filter = 8000000;
}
receiver_model.set_baseband_bandwidth(anti_alias_baseband_bandwidth_filter);
waterfall.on_show();
};
option_bandwidth.set_selected_index(7); // 500k
option_bandwidth.set_selected_index(7); // 500k, Preselected starting default option 500khz
receiver_model.set_modulation(ReceiverModel::Mode::Capture);
receiver_model.set_baseband_bandwidth(baseband_bandwidth);
receiver_model.enable();
record_view.on_error = [&nav](std::string message) {
+9 -2
View File
@@ -48,7 +48,7 @@ private:
static constexpr ui::Dim header_height = 3 * 16;
uint32_t sampling_rate = 0;
static constexpr uint32_t baseband_bandwidth = 2500000;
uint32_t anti_alias_baseband_bandwidth_filter = 2500000; // we rename the previous var , and change type static constexpr to normal var.
void on_tuning_frequency_changed(rf::Frequency f);
@@ -95,7 +95,14 @@ private:
{ " 50k ", 50000 },
{ "100k ", 100000 },
{ "250k ", 250000 },
{ "500k ", 500000 }
{ "500k ", 500000 }, // Previous Limit bandwith Option with perfect micro SD write .C16 format operaton.
{ "600k ", 600000 }, // That extended option is still possible to record with FW version Mayhem v1.41 (< 2,5MB/sec)
{ "750k ", 750000 }, // From that BW onwards, the LCD is ok, but the recorded file is auto decimated,(not real file size)
{ "1100k", 1100000 },
{ "1750k", 1750000 },
{ "2000k", 2000000 },
{ "2500k", 2500000 },
{ "2750k", 2750000 } // That is our max Capture option , to keep using later / 8 decimation (22Mhz sampling ADC)
}
};
+444
View File
@@ -0,0 +1,444 @@
// Part of dump1090, a Mode S message decoder for RTLSDR devices.
//
// dump1090.h: main program header
//
// Copyright (c) 2014-2016 Oliver Jowett <oliver@mutability.co.uk>
//
// This file is free software: you may copy, redistribute and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 2 of the License, or (at your
// option) any later version.
//
// This file is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// This file incorporates work covered by the following copyright and
// permission notice:
//
// Copyright (C) 2012 by Salvatore Sanfilippo <antirez@gmail.com>
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef __DUMP1090_H
#define __DUMP1090_H
// Default version number, if not overriden by the Makefile
#ifndef MODES_DUMP1090_VERSION
# define MODES_DUMP1090_VERSION "unknown"
#endif
#ifndef MODES_DUMP1090_VARIANT
# define MODES_DUMP1090_VARIANT "dump1090-unknown"
#endif
#define MODES_DEFAULT_FREQ 1090000000
#define MODES_DEFAULT_WIDTH 1000
#define MODES_DEFAULT_HEIGHT 700
#define MODES_RTL_BUFFERS 15 // Number of RTL buffers
#define MODES_RTL_BUF_SIZE (16*16384) // 256k
#define MODES_MAG_BUF_SAMPLES (MODES_RTL_BUF_SIZE / 2) // Each sample is 2 bytes
#define MODES_MAG_BUFFERS 12 // Number of magnitude buffers (should be smaller than RTL_BUFFERS for flowcontrol to work)
#define MODES_AUTO_GAIN -100 // Use automatic gain
#define MODES_MAX_GAIN 999999 // Use max available gain
#define MODES_MSG_SQUELCH_DB 4.0 // Minimum SNR, in dB
#define MODES_MSG_ENCODER_ERRS 3 // Maximum number of encoding errors
#define MODEAC_MSG_SAMPLES (25 * 2) // include up to the SPI bit
#define MODEAC_MSG_BYTES 2
#define MODEAC_MSG_SQUELCH_LEVEL 0x07FF // Average signal strength limit
#define MODES_PREAMBLE_US 8 // microseconds = bits
#define MODES_PREAMBLE_SAMPLES (MODES_PREAMBLE_US * 2)
#define MODES_PREAMBLE_SIZE (MODES_PREAMBLE_SAMPLES * sizeof(uint16_t))
#define MODES_LONG_MSG_BYTES 14
#define MODES_SHORT_MSG_BYTES 7
#define MODES_LONG_MSG_BITS (MODES_LONG_MSG_BYTES * 8)
#define MODES_SHORT_MSG_BITS (MODES_SHORT_MSG_BYTES * 8)
#define MODES_LONG_MSG_SAMPLES (MODES_LONG_MSG_BITS * 2)
#define MODES_SHORT_MSG_SAMPLES (MODES_SHORT_MSG_BITS * 2)
#define MODES_LONG_MSG_SIZE (MODES_LONG_MSG_SAMPLES * sizeof(uint16_t))
#define MODES_SHORT_MSG_SIZE (MODES_SHORT_MSG_SAMPLES * sizeof(uint16_t))
#define MODES_OS_PREAMBLE_SAMPLES (20)
#define MODES_OS_PREAMBLE_SIZE (MODES_OS_PREAMBLE_SAMPLES * sizeof(uint16_t))
#define MODES_OS_LONG_MSG_SAMPLES (268)
#define MODES_OS_SHORT_MSG_SAMPLES (135)
#define MODES_OS_LONG_MSG_SIZE (MODES_LONG_MSG_SAMPLES * sizeof(uint16_t))
#define MODES_OS_SHORT_MSG_SIZE (MODES_SHORT_MSG_SAMPLES * sizeof(uint16_t))
#define MODES_OUT_BUF_SIZE (1500)
#define MODES_OUT_FLUSH_SIZE (MODES_OUT_BUF_SIZE - 256)
#define MODES_OUT_FLUSH_INTERVAL (60000)
#define MODES_USER_LATLON_VALID (1<<0)
#define INVALID_ALTITUDE (-9999)
/* Where did a bit of data arrive from? In order of increasing priority */
typedef enum {
SOURCE_INVALID, /* data is not valid */
SOURCE_MODE_AC, /* A/C message */
SOURCE_MLAT, /* derived from mlat */
SOURCE_MODE_S, /* data from a Mode S message, no full CRC */
SOURCE_MODE_S_CHECKED, /* data from a Mode S message with full CRC */
SOURCE_TISB, /* data from a TIS-B extended squitter message */
SOURCE_ADSR, /* data from a ADS-R extended squitter message */
SOURCE_ADSB, /* data from a ADS-B extended squitter message */
} datasource_t;
/* What sort of address is this and who sent it?
* (Earlier values are higher priority)
*/
typedef enum {
ADDR_ADSB_ICAO, /* Mode S or ADS-B, ICAO address, transponder sourced */
ADDR_ADSB_ICAO_NT, /* ADS-B, ICAO address, non-transponder */
ADDR_ADSR_ICAO, /* ADS-R, ICAO address */
ADDR_TISB_ICAO, /* TIS-B, ICAO address */
ADDR_ADSB_OTHER, /* ADS-B, other address format */
ADDR_ADSR_OTHER, /* ADS-R, other address format */
ADDR_TISB_TRACKFILE, /* TIS-B, Mode A code + track file number */
ADDR_TISB_OTHER, /* TIS-B, other address format */
ADDR_MODE_A, /* Mode A */
ADDR_UNKNOWN /* unknown address format */
} addrtype_t;
typedef enum {
UNIT_FEET,
UNIT_METERS
} altitude_unit_t;
typedef enum {
UNIT_NAUTICAL_MILES,
UNIT_STATUTE_MILES,
UNIT_KILOMETERS,
} interactive_distance_unit_t;
typedef enum {
ALTITUDE_BARO,
ALTITUDE_GEOM
} altitude_source_t;
typedef enum {
AG_INVALID,
AG_GROUND,
AG_AIRBORNE,
AG_UNCERTAIN
} airground_t;
typedef enum {
SIL_INVALID, SIL_UNKNOWN, SIL_PER_SAMPLE, SIL_PER_HOUR
} sil_type_t;
typedef enum {
CPR_SURFACE, CPR_AIRBORNE, CPR_COARSE
} cpr_type_t;
typedef enum {
HEADING_INVALID, // Not set
HEADING_GROUND_TRACK, // Direction of track over ground, degrees clockwise from true north
HEADING_TRUE, // Heading, degrees clockwise from true north
HEADING_MAGNETIC, // Heading, degrees clockwise from magnetic north
HEADING_MAGNETIC_OR_TRUE, // HEADING_MAGNETIC or HEADING_TRUE depending on the HRD bit in opstatus
HEADING_TRACK_OR_HEADING // GROUND_TRACK / MAGNETIC / TRUE depending on the TAH bit in opstatus
} heading_type_t;
typedef enum {
COMMB_UNKNOWN,
COMMB_AMBIGUOUS,
COMMB_EMPTY_RESPONSE,
COMMB_DATALINK_CAPS,
COMMB_GICB_CAPS,
COMMB_AIRCRAFT_IDENT,
COMMB_ACAS_RA,
COMMB_VERTICAL_INTENT,
COMMB_TRACK_TURN,
COMMB_HEADING_SPEED
} commb_format_t;
typedef enum {
NAV_MODE_AUTOPILOT = 1,
NAV_MODE_VNAV = 2,
NAV_MODE_ALT_HOLD = 4,
NAV_MODE_APPROACH = 8,
NAV_MODE_LNAV = 16,
NAV_MODE_TCAS = 32
} nav_modes_t;
// Matches encoding of the ES type 28/1 emergency/priority status subfield
typedef enum {
EMERGENCY_NONE = 0,
EMERGENCY_GENERAL = 1,
EMERGENCY_LIFEGUARD = 2,
EMERGENCY_MINFUEL = 3,
EMERGENCY_NORDO = 4,
EMERGENCY_UNLAWFUL = 5,
EMERGENCY_DOWNED = 6,
EMERGENCY_RESERVED = 7
} emergency_t;
typedef enum {
NAV_ALT_INVALID, NAV_ALT_UNKNOWN, NAV_ALT_AIRCRAFT, NAV_ALT_MCP, NAV_ALT_FMS
} nav_altitude_source_t;
#define MODES_NON_ICAO_ADDRESS (1<<24) // Set on addresses to indicate they are not ICAO addresses
#define MODES_INTERACTIVE_REFRESH_TIME 250 // Milliseconds
#define MODES_INTERACTIVE_DISPLAY_TTL 60000 // Delete from display after 60 seconds
#define MODES_NET_HEARTBEAT_INTERVAL 60000 // milliseconds
#define MODES_CLIENT_BUF_SIZE 1024
#define MODES_NET_SNDBUF_SIZE (1024*64)
#define MODES_NET_SNDBUF_MAX (7)
#define HISTORY_SIZE 120
#define HISTORY_INTERVAL 30000
#define MODES_NOTUSED(V) ((void) V)
#define MAX_AMPLITUDE 65535.0
#define MAX_POWER (MAX_AMPLITUDE * MAX_AMPLITUDE)
#define FAUP_DEFAULT_RATE_MULTIPLIER 1.0 // FA Upload rate multiplier
//======================== structure declarations =========================
typedef enum {
SDR_NONE, SDR_IFILE, SDR_RTLSDR, SDR_BLADERF, SDR_HACKRF, SDR_LIMESDR
} sdr_type_t;
// The struct we use to store information about a decoded message.
struct modesMessage {
// Generic fields
unsigned char msg[MODES_LONG_MSG_BYTES]; // Binary message.
unsigned char verbatim[MODES_LONG_MSG_BYTES]; // Binary message, as originally received before correction
int msgbits; // Number of bits in message
int msgtype; // Downlink format #
uint32_t crc; // Message CRC
int correctedbits; // No. of bits corrected
uint32_t addr; // Address Announced
addrtype_t addrtype; // address format / source
uint64_t timestampMsg; // Timestamp of the message (12MHz clock)
uint64_t sysTimestampMsg; // Timestamp of the message (system time)
int remote; // If set this message is from a remote station
double signalLevel; // RSSI, in the range [0..1], as a fraction of full-scale power
int score; // Scoring from scoreModesMessage, if used
int reliable; // is this a "reliable" message (uncorrected DF11/DF17/DF18)?
datasource_t source; // Characterizes the overall message source
// Raw data, just extracted directly from the message
// The names reflect the field names in Annex 4
unsigned IID; // extracted from CRC of DF11s
unsigned AA;
unsigned AC;
unsigned CA;
unsigned CC;
unsigned CF;
unsigned DR;
unsigned FS;
unsigned ID;
unsigned KE;
unsigned ND;
unsigned RI;
unsigned SL;
unsigned UM;
unsigned VS;
unsigned char MB[7];
unsigned char MD[10];
unsigned char ME[7];
unsigned char MV[7];
// Decoded data
unsigned altitude_baro_valid : 1;
unsigned altitude_geom_valid : 1;
unsigned track_valid : 1;
unsigned track_rate_valid : 1;
unsigned heading_valid : 1;
unsigned roll_valid : 1;
unsigned gs_valid : 1;
unsigned ias_valid : 1;
unsigned tas_valid : 1;
unsigned mach_valid : 1;
unsigned baro_rate_valid : 1;
unsigned geom_rate_valid : 1;
unsigned squawk_valid : 1;
unsigned callsign_valid : 1;
unsigned cpr_valid : 1;
unsigned cpr_odd : 1;
unsigned cpr_decoded : 1;
unsigned cpr_relative : 1;
unsigned category_valid : 1;
unsigned geom_delta_valid : 1;
unsigned from_mlat : 1;
unsigned from_tisb : 1;
unsigned spi_valid : 1;
unsigned spi : 1;
unsigned alert_valid : 1;
unsigned alert : 1;
unsigned emergency_valid : 1;
unsigned metype; // DF17/18 ME type
unsigned mesub; // DF17/18 ME subtype
commb_format_t commb_format; // Inferred format of a comm-b message
// valid if altitude_baro_valid:
int altitude_baro; // Altitude in either feet or meters
altitude_unit_t altitude_baro_unit; // the unit used for altitude
// valid if altitude_geom_valid:
int altitude_geom; // Altitude in either feet or meters
altitude_unit_t altitude_geom_unit; // the unit used for altitude
// following fields are valid if the corresponding _valid field is set:
int geom_delta; // Difference between geometric and baro alt
float heading; // ground track or heading, degrees (0-359). Reported directly or computed from from EW and NS velocity
heading_type_t heading_type;// how to interpret 'track_or_heading'
float track_rate; // Rate of change of track, degrees/second
float roll; // Roll, degrees, negative is left roll
struct {
// Groundspeed, kts, reported directly or computed from from EW and NS velocity
// For surface movement, this has different interpretations for v0 and v2; both
// fields are populated. The tracking layer will update "gs.selected".
float v0;
float v2;
float selected;
} gs;
unsigned ias; // Indicated airspeed, kts
unsigned tas; // True airspeed, kts
double mach; // Mach number
int baro_rate; // Rate of change of barometric altitude, feet/minute
int geom_rate; // Rate of change of geometric (GNSS / INS) altitude, feet/minute
unsigned squawk; // 13 bits identity (Squawk), encoded as 4 hex digits
char callsign[9]; // 8 chars flight number, NUL-terminated
unsigned category; // A0 - D7 encoded as a single hex byte
emergency_t emergency; // emergency/priority status
// valid if cpr_valid
cpr_type_t cpr_type; // The encoding type used (surface, airborne, coarse TIS-B)
unsigned cpr_lat; // Non decoded latitude.
unsigned cpr_lon; // Non decoded longitude.
unsigned cpr_nucp; // NUCp/NIC value implied by message type
airground_t airground; // air/ground state
// valid if cpr_decoded:
double decoded_lat;
double decoded_lon;
unsigned decoded_nic;
unsigned decoded_rc;
// various integrity/accuracy things
struct {
unsigned nic_a_valid : 1;
unsigned nic_b_valid : 1;
unsigned nic_c_valid : 1;
unsigned nic_baro_valid : 1;
unsigned nac_p_valid : 1;
unsigned nac_v_valid : 1;
unsigned gva_valid : 1;
unsigned sda_valid : 1;
unsigned nic_a : 1; // if nic_a_valid
unsigned nic_b : 1; // if nic_b_valid
unsigned nic_c : 1; // if nic_c_valid
unsigned nic_baro : 1; // if nic_baro_valid
unsigned nac_p : 4; // if nac_p_valid
unsigned nac_v : 3; // if nac_v_valid
unsigned sil : 2; // if sil_type != SIL_INVALID
sil_type_t sil_type;
unsigned gva : 2; // if gva_valid
unsigned sda : 2; // if sda_valid
} accuracy;
// Operational Status
struct {
unsigned valid : 1;
unsigned version : 3;
unsigned om_acas_ra : 1;
unsigned om_ident : 1;
unsigned om_atc : 1;
unsigned om_saf : 1;
unsigned cc_acas : 1;
unsigned cc_cdti : 1;
unsigned cc_1090_in : 1;
unsigned cc_arv : 1;
unsigned cc_ts : 1;
unsigned cc_tc : 2;
unsigned cc_uat_in : 1;
unsigned cc_poa : 1;
unsigned cc_b2_low : 1;
unsigned cc_lw_valid : 1;
heading_type_t tah;
heading_type_t hrd;
unsigned cc_lw;
unsigned cc_antenna_offset;
} opstatus;
// combined:
// Target State & Status (ADS-B V2 only)
// Comm-B BDS4,0 Vertical Intent
struct {
unsigned heading_valid : 1;
unsigned fms_altitude_valid : 1;
unsigned mcp_altitude_valid : 1;
unsigned qnh_valid : 1;
unsigned modes_valid : 1;
float heading; // heading, degrees (0-359) (could be magnetic or true heading; magnetic recommended)
heading_type_t heading_type;
unsigned fms_altitude; // FMS selected altitude
unsigned mcp_altitude; // MCP/FCU selected altitude
float qnh; // altimeter setting (QFE or QNH/QNE), millibars
nav_altitude_source_t altitude_source;
nav_modes_t modes;
} nav;
};
#endif // __DUMP1090_H
+2
View File
@@ -50,6 +50,8 @@ struct ERTKey {
{
}
ERTKey( const ERTKey& other ) = default;
ERTKey& operator=(const ERTKey& other) {
id = other.id;
commodity_type = other.commodity_type;
+29 -16
View File
@@ -30,10 +30,11 @@ using namespace pocsag;
#include "string_format.hpp"
#include "utility.hpp"
#include "audio.hpp"
void POCSAGLogger::log_raw_data(const pocsag::POCSAGPacket& packet, const uint32_t frequency) {
std::string entry = "Raw: F:" + to_string_dec_uint(frequency) + "Hz " +
pocsag::bitrate_str(packet.bitrate()) + " Codewords:";
to_string_dec_uint(packet.bitrate()) + " Codewords:";
// Raw hex dump of all the codewords
for (size_t c = 0; c < 16; c++)
@@ -64,13 +65,13 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
add_children({
&rssi,
&channel,
&audio,
&field_rf_amp,
&field_lna,
&field_vga,
&field_frequency,
&options_bitrate,
&options_phase,
&check_log,
&field_volume,
&check_ignore,
&sym_ignore,
&console
@@ -99,14 +100,11 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
logging = v;
};
options_bitrate.on_change = [this](size_t, OptionsField::value_t v) {
on_config_changed(v, options_phase.selected_index_value());
};
options_bitrate.set_selected_index(1); // 1200bps
options_phase.on_change = [this](size_t, OptionsField::value_t v) {
on_config_changed(options_bitrate.selected_index_value(),v);
field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99);
field_volume.on_change = [this](int32_t v) {
this->on_headphone_volume_changed(v);
};
check_ignore.set_value(ignore);
check_ignore.on_select = [this](Checkbox&, bool v) {
ignore = v;
@@ -121,9 +119,16 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
logger = std::make_unique<POCSAGLogger>();
if (logger)
logger->append("pocsag.txt");
audio::output::start();
audio::output::unmute();
baseband::set_pocsag();
}
POCSAGAppView::~POCSAGAppView() {
audio::output::stop();
// Save ignored address
persistent_memory::set_pocsag_ignore_address(sym_ignore.value_dec_u32());
@@ -135,6 +140,12 @@ void POCSAGAppView::focus() {
field_frequency.focus();
}
void POCSAGAppView::on_headphone_volume_changed(int32_t v) {
const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max;
receiver_model.set_headphone_volume(new_volume);
}
// Useless ?
void POCSAGAppView::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
@@ -154,11 +165,17 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage * message) {
// " Ignored address " + to_string_dec_uint(pocsag_state.address));
return;
}
// Too many errors for reliable decode
if ((ignore) && (pocsag_state.errors >= 3)) {
return;
}
std::string console_info;
const uint32_t roundVal = 50;
const uint32_t bitrate = roundVal * ((message->packet.bitrate() + (roundVal/2))/roundVal);
console_info = "\n" + to_string_datetime(message->packet.timestamp(), HM);
console_info += " " + pocsag::bitrate_str(message->packet.bitrate());
console_info += " " + to_string_dec_uint(bitrate);
console_info += " ADDR:" + to_string_dec_uint(pocsag_state.address);
console_info += " F" + to_string_dec_uint(pocsag_state.function);
@@ -201,10 +218,6 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage * message) {
logger->log_raw_data(message->packet, target_frequency());
}
void POCSAGAppView::on_config_changed(const uint32_t new_bitrate, bool new_phase) {
baseband::set_pocsag(pocsag_bitrates[new_bitrate], new_phase);
}
void POCSAGAppView::set_target_frequency(const uint32_t new_value) {
target_frequency_ = new_value;
receiver_model.set_tuning_frequency(new_value);
+18 -25
View File
@@ -61,7 +61,7 @@ private:
static constexpr uint32_t initial_target_frequency = 466175000;
bool logging { true };
bool ignore { false };
bool ignore { true };
uint32_t last_address = 0xFFFFFFFF;
pocsag::POCSAGState pocsag_state { };
@@ -80,48 +80,41 @@ private:
Channel channel {
{ 21 * 8, 5, 6 * 8, 4 },
};
Audio audio{
{ 21 * 8, 10, 6 * 8, 4 },
};
FrequencyField field_frequency {
{ 0 * 8, 0 * 8 },
};
OptionsField options_bitrate {
{ 12 * 8, 21 },
7,
{
{ "512bps ", 0 },
{ "1200bps", 1 },
{ "2400bps", 2 }
}
};
OptionsField options_phase {
{ 6 * 8, 21 },
1,
{
{ "P", 0 },
{ "N", 1 },
}
};
Checkbox check_log {
{ 22 * 8, 21 },
{ 24 * 8, 21 },
3,
"LOG",
true
};
NumberField field_volume{
{ 28 * 8, 0 * 16 },
2,
{ 0, 99 },
1,
' ',
};
Checkbox check_ignore {
{ 1 * 8, 40 },
15,
"Ignore address:",
{ 1 * 8, 21 },
12,
"Ignore addr:",
true
};
SymField sym_ignore {
{ 19 * 8, 40 },
{ 16 * 8, 21 },
7,
SymField::SYMFIELD_DEC
};
Console console {
{ 0, 4 * 16, 240, 240 }
{ 0, 3 * 16, 240, 256 }
};
std::unique_ptr<POCSAGLogger> logger { };
@@ -132,7 +125,7 @@ private:
void on_packet(const POCSAGPacketMessage * message);
void on_config_changed(const uint32_t new_bitrate, const bool phase);
void on_headphone_volume_changed(int32_t v);
uint32_t target_frequency() const;
void set_target_frequency(const uint32_t new_value);
+9 -2
View File
@@ -139,7 +139,12 @@ void ReplayAppView::start() {
}
);
}
field_rfgain.on_change = [this](int32_t v) {
tx_gain = v;
};
field_rfgain.set_value(tx_gain);
receiver_model.set_tx_gain(tx_gain);
radio::enable({
receiver_model.tuning_frequency(),
sample_rate * 8 ,
@@ -180,6 +185,8 @@ ReplayAppView::ReplayAppView(
NavigationView& nav
) : nav_ (nav)
{
tx_gain = 35;field_rfgain.set_value(tx_gain);
baseband::run_image(portapack::spi_flash::image_tag_replay);
add_children({
@@ -190,7 +197,7 @@ ReplayAppView::ReplayAppView(
&text_duration,
&progressbar,
&field_frequency,
&field_lna,
&field_rfgain,
&field_rf_amp,
&check_loop,
&button_play,
+9 -3
View File
@@ -51,6 +51,7 @@ private:
static constexpr ui::Dim header_height = 3 * 16;
uint32_t sample_rate = 0;
int32_t tx_gain { 47 };
static constexpr uint32_t baseband_bandwidth = 2500000;
const size_t read_size { 16384 };
const size_t buffer_count { 3 };
@@ -75,7 +76,7 @@ private:
bool ready_signal { false };
Labels labels {
{ { 10 * 8, 2 * 16 }, "LNA: A:", Color::light_grey() }
{ { 10 * 8, 2 * 16 }, "GAIN A:", Color::light_grey() }
};
Button button_open {
@@ -103,8 +104,13 @@ private:
FrequencyField field_frequency {
{ 0 * 8, 2 * 16 },
};
LNAGainField field_lna {
{ 14 * 8, 2 * 16 }
NumberField field_rfgain {
{ 14 * 8, 2 * 16 },
2,
{ 0, 47 },
1,
' '
};
RFAmpField field_rf_amp {
{ 19 * 8, 2 * 16 }
+6 -1
View File
@@ -110,7 +110,11 @@ void SoundBoardView::start_tx(const uint32_t id) {
1536000 / 20, // Update vu-meter at 20Hz
transmitter_model.channel_bandwidth(),
0, // Gain is unused
TONES_F2D(tone_key_frequency(tone_key_index), 1536000)
TONES_F2D(tone_key_frequency(tone_key_index), 1536000),
0, //AM
0, //DSB
0, //USB
0 //LSB
);
baseband::set_sample_rate(sample_rate);
@@ -130,6 +134,7 @@ void SoundBoardView::start_tx(const uint32_t id) {
}*/
void SoundBoardView::on_tx_progress(const uint32_t progress) {
(void)progress ; // avoid warning
//progressbar.set_value(progress);
}
+11 -5
View File
@@ -6,7 +6,8 @@ namespace ui
{
add_children({&console, &button_ok});
button_ok.on_select = [&nav](Button &) {
button_ok.on_select = [&nav](Button &)
{
nav.pop();
};
@@ -28,9 +29,14 @@ namespace ui
console.writeln("\x1B\x06Mayhem:\x1B\x10");
console.writeln("eried,euquiq,gregoryfenton");
console.writeln("johnelder,jwetzell,nnemanjan00");
console.writeln("N0vaPixel,klockee,jamesshao8");
console.writeln("ITAxReal,rascafr,mcules");
console.writeln("dqs105,strijar");
console.writeln("N0vaPixel,klockee,GullCode");
console.writeln("jamesshao8,ITAxReal,rascafr");
console.writeln("mcules,dqs105,strijar");
console.writeln("zhang00963,RedFox-Fr,aldude999");
console.writeln("East2West,fossum,ArjanOnwezen");
console.writeln("vXxOinvizioNxX,teixeluis");
console.writeln("Brumi-2021,texasyojimbo");
console.writeln("heurist1,intoxsick");
console.writeln("");
break;
@@ -74,4 +80,4 @@ namespace ui
button_ok.focus();
}
} /* namespace ui */
} /* namespace ui */
+104 -35
View File
@@ -20,6 +20,8 @@
* Boston, MA 02110-1301, USA.
*/
#include <strings.h>
#include "ui_adsb_rx.hpp"
#include "ui_alphanum.hpp"
@@ -47,7 +49,7 @@ void RecentEntriesTable<AircraftRecentEntries>::draw(
if (entry_age < ADSB_DECAY_A) {
aged_color = 0x10;
target_color = Color::green();
} else if ((entry_age >= ADSB_DECAY_A) && (entry_age < ADSB_DECAY_B)) {
} else if (entry_age < ADSB_DECAY_B) {
aged_color = 0x07;
target_color = Color::light_grey();
} else {
@@ -57,10 +59,21 @@ void RecentEntriesTable<AircraftRecentEntries>::draw(
std::string entry_string = "\x1B";
entry_string += aged_color;
#if false
entry_string += to_string_hex(entry.ICAO_address, 6) + " " +
entry.callsign + " " +
(entry.hits <= 999 ? to_string_dec_uint(entry.hits, 4) : "999+") + " " +
entry.time_string;
#else
// SBT
entry_string +=
(entry.callsign[0]!=' ' ? entry.callsign + " " : to_string_hex(entry.ICAO_address, 6) + " ") +
to_string_dec_uint((unsigned int)((entry.pos.altitude+50)/100),4) +
to_string_dec_uint((unsigned int)entry.velo.speed,4) +
to_string_dec_uint((unsigned int)(entry.amp>>9),4) + " " +
(entry.hits <= 999 ? to_string_dec_uint(entry.hits, 3) + " " : "1k+ ") +
to_string_dec_uint(entry.age, 3);
#endif
painter.draw_string(
target_rect.location(),
@@ -69,7 +82,7 @@ void RecentEntriesTable<AircraftRecentEntries>::draw(
);
if (entry.pos.valid)
painter.draw_bitmap(target_rect.location() + Point(15 * 8, 0), bitmap_target, target_color, style.background);
painter.draw_bitmap(target_rect.location() + Point(8 * 8, 0), bitmap_target, target_color, style.background);
}
void ADSBLogger::log_str(std::string& logline) {
@@ -117,6 +130,7 @@ ADSBRxDetailsView::ADSBRxDetailsView(
{
char file_buffer[32] { 0 };
bool found = false;
int number_of_airlines = 0;
std::string airline_code;
size_t c;
@@ -140,7 +154,8 @@ ADSBRxDetailsView::ADSBRxDetailsView(
// Try getting the airline's name from airlines.db
auto result = db_file.open("ADSB/airlines.db");
if (!result.is_valid()) {
// Search for 3-letter code in 0x0000~0x2000
// Search for 3-letter code
number_of_airlines = (db_file.size() / 68); // determine number of airlines in file
airline_code = entry_copy.callsign.substr(0, 3);
c = 0;
do {
@@ -151,10 +166,10 @@ ADSBRxDetailsView::ADSBRxDetailsView(
found = true;
else
c++;
} while (!found);
} while (!found && (c < number_of_airlines));
if (found) {
db_file.seek(0x2000 + (c << 6));
db_file.seek((number_of_airlines * 4) + (c << 6)); // seek starting after index
db_file.read(file_buffer, 32);
text_airline.set(file_buffer);
db_file.read(file_buffer, 32);
@@ -171,17 +186,19 @@ ADSBRxDetailsView::ADSBRxDetailsView(
text_callsign.set(entry_copy.callsign);
button_see_map.on_select = [this, &nav](Button&) {
geomap_view = nav.push<GeoMapView>(
entry_copy.callsign,
entry_copy.pos.altitude,
GeoPos::alt_unit::FEET,
entry_copy.pos.latitude,
entry_copy.pos.longitude,
entry_copy.velo.heading,
[this]() {
if (!send_updates) { // Prevent recursivley launching the map
geomap_view = nav.push<GeoMapView>(
entry_copy.callsign,
entry_copy.pos.altitude,
GeoPos::alt_unit::FEET,
entry_copy.pos.latitude,
entry_copy.pos.longitude,
entry_copy.velo.heading,
[this]() {
send_updates = false;
});
send_updates = true;
send_updates = true;
}
};
};
@@ -190,11 +207,41 @@ void ADSBRxView::focus() {
}
ADSBRxView::~ADSBRxView() {
receiver_model.set_tuning_frequency(prevFreq);
rtc_time::signal_tick_second -= signal_token_tick_second;
receiver_model.disable();
baseband::shutdown();
}
AircraftRecentEntry ADSBRxView::find_or_create_entry(uint32_t ICAO_address) {
auto it = find(recent, ICAO_address);
// If not found
if (it == std::end(recent)){
recent.emplace_front(ICAO_address); // Add it
truncate_entries(recent); // Truncate the list
sort_entries_by_state();
it = find(recent, ICAO_address); // Find it again
}
return *it;
}
void ADSBRxView::replace_entry(AircraftRecentEntry & entry)
{
uint32_t ICAO_address = entry.ICAO_address;
std::replace_if( recent.begin(), recent.end(),
[ICAO_address](const AircraftRecentEntry & compEntry) {return ICAO_address == compEntry.ICAO_address;},
entry);
}
void ADSBRxView::sort_entries_by_state()
{
// Sorting List pn age_state using lambda function as comparator
recent.sort([](const AircraftRecentEntry & left, const AircraftRecentEntry & right){return (left.age_state < right.age_state); });
}
void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
rtc::RTC datetime;
std::string str_timestamp;
@@ -205,11 +252,17 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
auto frame = message->frame;
uint32_t ICAO_address = frame.get_ICAO_address();
if (frame.check_CRC() && frame.get_ICAO_address()) {
if (frame.check_CRC() && ICAO_address) {
rtcGetTime(&RTCD1, &datetime);
auto& entry = ::on_packet(recent, ICAO_address);
auto entry = find_or_create_entry(ICAO_address);
frame.set_rx_timestamp(datetime.minute() * 60 + datetime.second());
entry.reset_age();
if (entry.hits==0)
{
entry.amp = message->amp;
} else {
entry.amp = ((entry.amp*15)+message->amp)>>4;
}
str_timestamp = to_string_datetime(datetime, HMS);
entry.set_time_string(str_timestamp);
@@ -222,36 +275,47 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
uint8_t msg_sub = frame.get_msg_sub();
uint8_t * raw_data = frame.get_raw_data();
if ((msg_type >= 1) && (msg_type <= 4)) {
// 4: // surveillance, altitude reply
if ((msg_type >= AIRCRAFT_ID_L) && (msg_type <= AIRCRAFT_ID_H)) {
callsign = decode_frame_id(frame);
entry.set_callsign(callsign);
logentry+=callsign+" ";
} else if (((msg_type >= 9) && (msg_type <= 18)) || ((msg_type >= 20) && (msg_type <= 22))) {
}
// 9:
// 18: { // Extended squitter/non-transponder
// 21: // Comm-B, identity reply
// 20: // Comm-B, altitude reply
else if (((msg_type >= AIRBORNE_POS_BARO_L) && (msg_type <= AIRBORNE_POS_BARO_H)) ||
((msg_type >= AIRBORNE_POS_GPS_L) && (msg_type <= AIRBORNE_POS_GPS_H))) {
entry.set_frame_pos(frame, raw_data[6] & 4);
if (entry.pos.valid) {
str_info = "Alt:" + to_string_dec_int(entry.pos.altitude) +
" Lat:" + to_string_dec_int(entry.pos.latitude) +
"." + to_string_dec_int((int)abs(entry.pos.latitude * 1000) % 100, 2, '0') +
" Lon:" + to_string_dec_int(entry.pos.longitude) +
"." + to_string_dec_int((int)abs(entry.pos.longitude * 1000) % 100, 2, '0');
entry.set_info_string(str_info);
logentry+=str_info+ " ";
" Lat:" + to_string_decimal(entry.pos.latitude, 2) +
" Lon:" + to_string_decimal(entry.pos.longitude, 2);
// printing the coordinates in the log file with more
// resolution, as we are not constrained by screen
// real estate there:
std::string log_info = "Alt:" + to_string_dec_int(entry.pos.altitude) +
" Lat:" + to_string_decimal(entry.pos.latitude, 7) +
" Lon:" + to_string_decimal(entry.pos.longitude, 7);
entry.set_info_string(str_info);
logentry+=log_info + " ";
if (send_updates)
details_view->update(entry);
}
} else if(msg_type == 19 && msg_sub >= 1 && msg_sub <= 4){
} else if(msg_type == AIRBORNE_VEL && msg_sub >= VEL_GND_SUBSONIC && msg_sub <= VEL_AIR_SUPERSONIC){
entry.set_frame_velo(frame);
logentry += "Type:" + to_string_dec_uint(msg_sub) +
" Hdg:" + to_string_dec_uint(entry.velo.heading) +
" Spd: "+ to_string_dec_int(entry.velo.speed);
if (send_updates)
details_view->update(entry);
}
}
recent_entries_view.set_dirty();
replace_entry(entry);
logger = std::make_unique<ADSBLogger>();
if (logger) {
@@ -269,13 +333,16 @@ void ADSBRxView::on_tick_second() {
entry.inc_age();
if (details_view) {
if (send_updates && (entry.key() == detailed_entry_key))
if (send_updates && (entry.key() == detailed_entry_key)) // Check if the ICAO address match
details_view->update(entry);
} else {
if ((entry.age == ADSB_DECAY_A) || (entry.age == ADSB_DECAY_B))
recent_entries_view.set_dirty();
}
}
// Sort the list if it is being displayed
if (!send_updates) {
sort_entries_by_state();
recent_entries_view.set_dirty();
}
}
ADSBRxView::ADSBRxView(NavigationView& nav) {
@@ -304,6 +371,8 @@ ADSBRxView::ADSBRxView(NavigationView& nav) {
on_tick_second();
};
prevFreq = receiver_model.tuning_frequency();
baseband::set_adsb();
receiver_model.set_tuning_frequency(1090000000);
+59 -6
View File
@@ -32,13 +32,39 @@
#include "adsb.hpp"
#include "message.hpp"
#include "crc.hpp"
using namespace adsb;
namespace ui {
#define ADSB_DECAY_A 10 // In seconds
#define ADSB_DECAY_B 30
#define ADSB_DECAY_C 60 // Can be used for removing old entries, RecentEntries already caps to 64
#define ADSB_DECAY_A 10 // In seconds
#define ADSB_DECAY_B 30
#define ADSB_DECAY_C 60 // Can be used for removing old entries, RecentEntries already caps to 64
#define AIRCRAFT_ID_L 1 // aircraft ID message type (lowest type id)
#define AIRCRAFT_ID_H 4 // aircraft ID message type (highest type id)
#define SURFACE_POS_L 5 // surface position (lowest type id)
#define SURFACE_POS_H 8 // surface position (highest type id)
#define AIRBORNE_POS_BARO_L 9 // airborne position (lowest type id)
#define AIRBORNE_POS_BARO_H 18 // airborne position (highest type id)
#define AIRBORNE_VEL 19 // airborne velocities
#define AIRBORNE_POS_GPS_L 20 // airborne position (lowest type id)
#define AIRBORNE_POS_GPS_H 22 // airborne position (highest type id)
#define RESERVED_L 23 // reserved for other uses
#define RESERVED_H 31 // reserved for other uses
#define VEL_GND_SUBSONIC 1
#define VEL_GND_SUPERSONIC 2
#define VEL_AIR_SUBSONIC 3
#define VEL_AIR_SUPERSONIC 4
#define O_E_FRAME_TIMEOUT 20 // timeout between odd and even frames
struct AircraftRecentEntry {
using Key = uint32_t;
@@ -47,9 +73,12 @@ struct AircraftRecentEntry {
uint32_t ICAO_address { };
uint16_t hits { 0 };
uint16_t age_state { 1 };
uint32_t age { 0 };
uint32_t amp { 0 };
adsb_pos pos { false, 0, 0, 0 };
adsb_vel velo { false, 0, 999 };
adsb_vel velo { false, 0, 999, 0 };
ADSBFrame frame_pos_even { };
ADSBFrame frame_pos_odd { };
@@ -82,7 +111,7 @@ struct AircraftRecentEntry {
frame_pos_odd = frame;
if (!frame_pos_even.empty() && !frame_pos_odd.empty()) {
if (abs(frame_pos_even.get_rx_timestamp() - frame_pos_odd.get_rx_timestamp()) < 20)
if (abs(frame_pos_even.get_rx_timestamp() - frame_pos_odd.get_rx_timestamp()) < O_E_FRAME_TIMEOUT)
pos = decode_frame_pos(frame_pos_even, frame_pos_odd);
}
}
@@ -98,13 +127,21 @@ struct AircraftRecentEntry {
void set_time_string(std::string& new_time_string) {
time_string = new_time_string;
}
void reset_age() {
age = 0;
}
void inc_age() {
age++;
if (age < ADSB_DECAY_A)
{
age_state = pos.valid ? 0 : 1;
}
else
{
age_state = (age < ADSB_DECAY_B) ? 2 : 3;
}
}
};
@@ -137,6 +174,8 @@ public:
void update(const AircraftRecentEntry& entry);
std::string title() const override { return "Details"; };
AircraftRecentEntry get_current_entry() { return entry_copy; }
private:
AircraftRecentEntry entry_copy { 0 };
@@ -213,16 +252,30 @@ public:
std::string title() const override { return "ADS-B receive"; };
void replace_entry(AircraftRecentEntry & entry);
AircraftRecentEntry find_or_create_entry(uint32_t ICAO_address);
void sort_entries_by_state();
private:
rf::Frequency prevFreq;
std::unique_ptr<ADSBLogger> logger { };
void on_frame(const ADSBFrameMessage * message);
void on_tick_second();
const RecentEntriesColumns columns { {
#if false
{ "ICAO", 6 },
{ "Callsign", 9 },
{ "Hits", 4 },
{ "Time", 8 }
#else
{ "ICAO/Call", 9 },
{ "Lvl", 3 },
{ "Spd", 3 },
{ "Amp", 3 },
{ "Hit", 3 },
{ "Age", 3 }
#endif
} };
AircraftRecentEntries recent { };
RecentEntriesView<RecentEntries<AircraftRecentEntry>> recent_entries_view { columns, recent };
+409
View File
@@ -0,0 +1,409 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_aprs_rx.hpp"
#include "audio.hpp"
#include "rtc_time.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "portapack_persistent_memory.hpp"
using namespace portapack;
void APRSLogger::log_raw_data(const std::string& data) {
rtc::RTC datetime;
rtcGetTime(&RTCD1, &datetime);
log_file.write_entry(datetime, data);
}
namespace ui {
template<>
void RecentEntriesTable<APRSRecentEntries>::draw(
const Entry& entry,
const Rect& target_rect,
Painter& painter,
const Style& style
) {
char aged_color;
Color target_color;
// auto entry_age = entry.age;
target_color = Color::green();
aged_color = 0x10;
std::string entry_string = "\x1B";
entry_string += aged_color;
entry_string += entry.source_formatted;
entry_string.append(10 - entry.source_formatted.size(),' ');
entry_string += " ";
entry_string += (entry.hits <= 999 ? to_string_dec_uint(entry.hits, 4) : "999+");
entry_string += " ";
entry_string += entry.time_string;
painter.draw_string(
target_rect.location(),
style,
entry_string
);
if (entry.has_position){
painter.draw_bitmap(target_rect.location() + Point(12 * 8, 0), bitmap_target, target_color, style.background);
}
}
void APRSRxView::focus() {
options_region.focus();
}
void APRSRxView::update_freq(rf::Frequency f) {
receiver_model.set_tuning_frequency(f);
}
APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect) : View(parent_rect) {
baseband::run_image(portapack::spi_flash::image_tag_aprs_rx);
add_children({
&rssi,
&channel,
&field_rf_amp,
&field_lna,
&field_vga,
&options_region,
&field_frequency,
&record_view,
&console
});
// DEBUG
record_view.on_error = [&nav](std::string message) {
nav.display_modal("Error", message);
};
record_view.set_sampling_rate(24000);
options_region.on_change = [this](size_t, int32_t i) {
if (i == 0){
field_frequency.set_value(144390000);
}
if(i == 1){
field_frequency.set_value(144800000);
}
if(i == 2){
field_frequency.set_value(145175000);
}
};
field_frequency.set_value(receiver_model.tuning_frequency());
field_frequency.set_step(100);
field_frequency.on_change = [this](rf::Frequency f) {
update_freq(f);
};
field_frequency.on_edit = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
new_view->on_changed = [this](rf::Frequency f) {
update_freq(f);
field_frequency.set_value(f);
};
};
options_region.set_selected_index(0, true);
logger = std::make_unique<APRSLogger>();
if (logger)
logger->append("APRS_RX_LOG.TXT");
baseband::set_aprs(1200);
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
receiver_model.set_sampling_rate(3072000);
receiver_model.set_baseband_bandwidth(1750000);
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
receiver_model.enable();
}
void APRSRxView::on_packet(const APRSPacketMessage* message){
std::string str_console = "\x1B";
aprs::APRSPacket packet = message->packet;
std::string stream_text = packet.get_stream_text();
str_console += (char)((console_color++ & 3) + 9);
str_console += stream_text;
if(logger){
logger->log_raw_data(stream_text);
}
//if(reset_console){ //having more than one console causes issues when switching tabs where one is disabled, and the other enabled breaking the scoll setup.
// console.on_hide();
// console.on_show();
// reset_console = false;
//}
console.writeln(str_console);
}
void APRSRxView::on_data(uint32_t value, bool is_data) {
std::string str_console = "\x1B";
std::string str_byte = "";
if (is_data) {
// Colorize differently after message splits
str_console += (char)((console_color & 3) + 9);
if (value == '\n') {
// Message split
console.writeln("");
console_color++;
if (logger) {
logger->log_raw_data(str_log);
str_log = "";
}
}
else {
if ((value >= 32) && (value < 127)) {
str_console += (char)value; // Printable
str_byte += (char)value;
} else {
str_console += "[" + to_string_hex(value, 2) + "]"; // Not printable
str_byte += "[" + to_string_hex(value, 2) + "]";
}
console.write(str_console);
if (logger) str_log += str_byte;
}
} else {
}
}
void APRSRxView::on_show(){
//some bug where the display scroll area is set to the entire screen when switching from the list tab with details showing back to the stream view.
//reset_console = true;
}
APRSRxView::~APRSRxView() {
audio::output::stop();
receiver_model.disable();
baseband::shutdown();
}
void APRSTableView::on_show_list() {
details_view.hidden(true);
recent_entries_view.hidden(false);
send_updates = false;
focus();
}
void APRSTableView::on_show_detail(const APRSRecentEntry& entry) {
recent_entries_view.hidden(true);
details_view.hidden(false);
details_view.set_entry(entry);
details_view.update();
details_view.focus();
detailed_entry_key = entry.key();
send_updates = true;
}
APRSTableView::APRSTableView(NavigationView& nav, Rect parent_rec) : View(parent_rec), nav_ {nav} {
add_children({
&recent_entries_view,
&details_view
});
hidden(true);
details_view.hidden(true);
recent_entries_view.set_parent_rect({0, 0, 240, 280});
details_view.set_parent_rect({0, 0, 240, 280});
recent_entries_view.on_select = [this](const APRSRecentEntry& entry) {
this->on_show_detail(entry);
};
details_view.on_close = [this]() {
this->on_show_list();
};
/* for(size_t i = 0; i <32 ; i++){
std::string id = "test" + i;
auto& entry = ::on_packet(recent, i);
entry.set_source_formatted(id);
}
recent_entries_view.set_dirty(); */
/*
std::string str1 = "test1";
std::string str12 = "test2";
std::string str13 = "test2";
auto& entry = ::on_packet(recent, 0x1);
entry.set_source_formatted(str1);
auto& entry2 = ::on_packet(recent, 0x2);
entry2.set_source_formatted(str12);
auto& entry3 = ::on_packet(recent, 0x2);
entry2.set_source_formatted(str13);
recent_entries_view.set_dirty();
*/
}
void APRSTableView::on_pkt(const APRSPacketMessage* message){
aprs::APRSPacket packet = message->packet;
rtc::RTC datetime;
std::string str_timestamp;
std::string source_formatted = packet.get_source_formatted();
std::string info_string = packet.get_stream_text();
rtcGetTime(&RTCD1, &datetime);
auto& entry = ::on_packet(recent, packet.get_source());
entry.reset_age();
entry.inc_hit();
str_timestamp = to_string_datetime(datetime, HMS);
entry.set_time_string(str_timestamp);
entry.set_info_string(info_string);
entry.set_source_formatted(source_formatted);
if(entry.has_position && !packet.has_position()){
//maintain position info
}
else {
entry.set_has_position(packet.has_position());
entry.set_pos(packet.get_position());
}
if( entry.key() == details_view.entry().key() ) {
details_view.set_entry(entry);
details_view.update();
}
recent_entries_view.set_dirty();
}
void APRSTableView::on_show(){
on_show_list();
}
void APRSTableView::on_hide(){
details_view.hidden(true);
}
void APRSTableView::focus(){
recent_entries_view.focus();
}
APRSTableView::~APRSTableView(){
}
void APRSDetailsView::focus() {
button_done.focus();
}
void APRSDetailsView::set_entry(const APRSRecentEntry& entry){
entry_copy = entry;
}
void APRSDetailsView::update() {
if(!hidden()){
//uint32_t age = entry_copy.age;
console.clear(true);
console.write(entry_copy.info_string);
button_see_map.hidden(!entry_copy.has_position);
}
if (send_updates)
geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, 0);
}
APRSDetailsView::~APRSDetailsView() {
}
APRSDetailsView::APRSDetailsView(
NavigationView& nav
)
{
add_children({
&console,
&button_done,
&button_see_map
});
button_done.on_select = [this, &nav](Button&) {
console.clear(true);
this->on_close();
};
button_see_map.on_select = [this, &nav](Button&) {
geomap_view = nav.push<GeoMapView>(
entry_copy.source_formatted,
0, //entry_copy.pos.altitude,
GeoPos::alt_unit::FEET,
entry_copy.pos.latitude,
entry_copy.pos.longitude,
0, /*entry_copy.velo.heading,*/
[this]() {
send_updates = false;
hidden(false);
update();
});
send_updates = true;
hidden(true);
};
};
APRSRXView::APRSRXView(NavigationView& nav) : nav_ {nav} {
add_children({
&tab_view,
&view_stream,
&view_table
});
}
void APRSRXView::focus(){
tab_view.focus();
}
APRSRXView::~APRSRXView() {
}
} /* namespace ui */
+276
View File
@@ -0,0 +1,276 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_APRS_RX_H__
#define __UI_APRS_RX_H__
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_record_view.hpp" // DEBUG
#include "ui_geomap.hpp"
#include "recent_entries.hpp"
#include "ui_tabview.hpp"
#include "log_file.hpp"
#include "utility.hpp"
class APRSLogger {
public:
Optional<File::Error> append(const std::string& filename) {
return log_file.append(filename);
}
void log_raw_data(const std::string& data);
private:
LogFile log_file { };
};
namespace ui {
struct APRSRecentEntry {
using Key = uint64_t;
static constexpr Key invalid_key = 0xffffffffffffffff;
uint16_t hits { 0 };
uint32_t age { 0 };
uint64_t source;
std::string source_formatted { " " };
std::string time_string { "" };
std::string info_string { "" };
aprs::aprs_pos pos;
bool has_position = false;
APRSRecentEntry(uint64_t src)
{
source = src;
}
Key key() const {
return source;
}
void set_source_formatted(std::string& new_source) {
source_formatted = new_source;
}
void inc_hit() {
hits++;
}
void set_info_string(std::string& new_info_string) {
info_string = new_info_string;
}
void set_time_string(std::string& new_time_string) {
time_string = new_time_string;
}
void set_pos(aprs::aprs_pos pos_in){
pos = pos_in;
}
void set_has_position(bool has_pos){
has_position = has_pos;
}
void reset_age() {
age = 0;
}
void inc_age() {
age++;
}
};
class APRSDetailsView : public View {
public:
APRSDetailsView(NavigationView&);
~APRSDetailsView();
APRSDetailsView(const APRSDetailsView&) = delete;
APRSDetailsView(APRSDetailsView&&) = delete;
APRSDetailsView& operator=(const APRSDetailsView&) = delete;
APRSDetailsView& operator=(APRSDetailsView&&) = delete;
void focus() override;
void update();
void set_entry(const APRSRecentEntry& entry);
const APRSRecentEntry& entry() const { return entry_copy; };
std::string title() const override { return "Details"; };
std::function<void(void)> on_close { };
private:
APRSRecentEntry entry_copy { 0 };
GeoMapView* geomap_view { nullptr };
bool send_updates { false };
Console console {
{ 0, 0 * 16, 240, 224 }
};
Button button_done {
{ 160, 14 * 16, 8 * 8, 3 * 16 },
"Close"
};
Button button_see_map {
{ 80, 14 * 16, 8 * 8, 3 * 16 },
"Map"
};
};
using APRSRecentEntries = RecentEntries<APRSRecentEntry>;
class APRSTableView: public View {
public:
APRSTableView(NavigationView& nav, Rect parent_rec);
~APRSTableView();
void on_show() override;
void on_hide() override;
void focus() override;
void on_pkt(const APRSPacketMessage* message);
std::string title() const override { return "Stations"; };
private:
NavigationView& nav_;
const RecentEntriesColumns columns { {
{ "Source", 9 },
{ "Loc", 6 },
{ "Hits", 4 },
{ "Time", 8 }
} };
APRSRecentEntries recent { };
RecentEntriesView<RecentEntries<APRSRecentEntry>> recent_entries_view { columns, recent };
APRSDetailsView details_view { nav_ };
uint32_t detailed_entry_key { 0 };
bool send_updates { false };
void on_show_list();
void on_show_detail(const APRSRecentEntry& entry);
};
class APRSRxView : public View {
public:
APRSRxView(NavigationView& nav, Rect parent_rect);
~APRSRxView();
void on_show() override;
void focus() override;
std::string title() const override { return "APRS RX"; };
void on_packet(const APRSPacketMessage* message);
private:
void on_data(uint32_t value, bool is_data);
bool reset_console = false;
uint8_t console_color { 0 };
std::string str_log { "" };
RFAmpField field_rf_amp {
{ 13 * 8, 0 * 16 }
};
LNAGainField field_lna {
{ 15 * 8, 0 * 16 }
};
VGAGainField field_vga {
{ 18 * 8, 0 * 16 }
};
RSSI rssi {
{ 21 * 8, 0, 6 * 8, 4 },
};
Channel channel {
{ 21 * 8, 5, 6 * 8, 4 },
};
OptionsField options_region {
{ 0 * 8, 0 * 8 },
3,
{
{ "NA ", 0 },
{ "EUR", 1 },
{ "AUS", 2 }
}
};
FrequencyField field_frequency {
{ 3 * 8, 0 * 16 },
};
// DEBUG
RecordView record_view {
{ 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
u"AFS_????", RecordView::FileType::WAV, 4096, 4
};
Console console {
{ 0, 2 * 16, 240, 240 }
};
void update_freq(rf::Frequency f);
std::unique_ptr<APRSLogger> logger { };
};
class APRSRXView : public View {
public:
APRSRXView(NavigationView& nav);
~APRSRXView();
void focus() override;
std::string title() const override { return "APRS RX"; };
private:
NavigationView& nav_;
Rect view_rect = { 0, 3 * 8, 240, 280 };
APRSRxView view_stream { nav_, view_rect };
APRSTableView view_table { nav_, view_rect };
TabView tab_view {
{ "Stream", Color::cyan(), &view_stream },
{ "List", Color::yellow(), &view_table }
};
MessageHandlerRegistration message_handler_packet {
Message::ID::APRSPacket,
[this](Message* const p) {
const auto message = static_cast<const APRSPacketMessage*>(p);
this->view_stream.on_packet(message);
this->view_table.on_pkt(message);
}
};
};
} /* namespace ui */
#endif/*__UI_APRS_RX_H__*/
+1
View File
@@ -82,6 +82,7 @@ private:
6,
SymField::SYMFIELD_ALPHANUM
};
NumberField num_ssid_dest {
{ 19 * 8, 2 * 16 },
2,
+2 -1
View File
@@ -121,7 +121,8 @@ void TemperatureWidget::paint(Painter& painter) {
}
TemperatureWidget::temperature_t TemperatureWidget::temperature(const sample_t sensor_value) const {
return -35 + sensor_value * 4; //max2837 datasheet temp 25ºC has sensor value: 15
/*It seems to be a temperature difference of 25C*/
return -40 +(sensor_value * 4.31)+25; //max2837 datasheet temp 25ºC has sensor value: 15
}
std::string TemperatureWidget::temperature_str(const temperature_t temperature) const {
+1
View File
@@ -216,6 +216,7 @@ FileSaveView::FileSaveView(
}*/
void FileLoadView::refresh_widgets(const bool v) {
(void)v; //avoid unused warning
set_dirty();
}
+3
View File
@@ -48,6 +48,9 @@ FreqManBaseView::FreqManBaseView(
} else
error_ = ERROR_NOFILES;
// initialize
change_category(last_category_id);
// Default function
on_change_category = [this](int32_t category_id) {
change_category(category_id);
+5 -5
View File
@@ -64,7 +64,7 @@ protected:
OptionsField options_category {
{ 9 * 8, 4 },
12,
14,
{ }
};
@@ -103,19 +103,19 @@ private:
};
Labels labels {
{ { 2 * 8, 14 * 8 }, "Save as:", Color::white() }
{ { 1 * 8, 12 * 8 }, "Save as:", Color::white() }
};
Button button_save_name {
{ 2 * 8, 17 * 8, 14 * 8, 48 },
{ 1 * 8, 17 * 8, 12 * 8, 48 },
"Name (set)"
};
Button button_save_timestamp {
{ 2 * 8, 25 * 8, 14 * 8, 48 },
{ 1 * 8, 25 * 8, 12 * 8, 48 },
"Timestamp:"
};
LiveDateTime live_timestamp {
{ 17 * 8, 27 * 8, 11 * 8, 16 }
{ 14 * 8, 27 * 8, 16 * 8, 16 }
};
};
+61 -1
View File
@@ -274,6 +274,7 @@ void JammerView::start_tx() {
transmitter_model.enable();
baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value());
mscounter = 0; //euquiq: Reset internal ms counter for do_timer()
} else {
if (out_of_ranges)
nav_.display_modal("Error", "Jamming bandwidth too large.\nMust be less than 24MHz.");
@@ -289,6 +290,59 @@ void JammerView::stop_tx() {
radio::disable();
baseband::set_jammer(false, JammerType::TYPE_FSK, 0);
jamming = false;
cooling = false;
}
//called each 1/60th of second
void JammerView::on_timer() {
if (++mscounter == 60) {
mscounter = 0;
if (jamming)
{
if (cooling)
{
if (++seconds >= field_timepause.value())
{ //Re-start TX
transmitter_model.enable();
button_transmit.set_text("STOP");
baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value());
int32_t jitter_amount = field_jitter.value();
if (jitter_amount)
{
lfsr_v = lfsr_iterate(lfsr_v);
jitter_amount = (jitter_amount / 2) - (lfsr_v & jitter_amount);
mscounter += jitter_amount;
}
cooling = false;
seconds = 0;
}
}
else
{
if (++seconds >= field_timetx.value()) //Start cooling period:
{
transmitter_model.disable();
button_transmit.set_text("PAUSED");
baseband::set_jammer(false, JammerType::TYPE_FSK, 0);
int32_t jitter_amount = field_jitter.value();
if (jitter_amount)
{
lfsr_v = lfsr_iterate(lfsr_v);
jitter_amount = (jitter_amount / 2) - (lfsr_v & jitter_amount);
mscounter += jitter_amount;
}
cooling = true;
seconds = 0;
}
}
}
}
}
JammerView::JammerView(
@@ -309,6 +363,9 @@ JammerView::JammerView(
&text_range_total,
&options_speed,
&options_hop,
&field_timetx,
&field_timepause,
&field_jitter,
&button_transmit
});
@@ -321,8 +378,11 @@ JammerView::JammerView(
options_hop.set_selected_index(1); // 50ms
button_transmit.set_style(&style_val);
field_timetx.set_value(30);
field_timepause.set_value(1);
button_transmit.on_select = [this](Button&) {
if (jamming)
if (jamming || cooling)
stop_tx();
else
start_tx();
+61 -84
View File
@@ -28,6 +28,7 @@
#include "transmitter_model.hpp"
#include "message.hpp"
#include "jammer.hpp"
#include "lfsr_random.hpp"
using namespace jammer;
@@ -57,78 +58,11 @@ private:
.foreground = Color::grey(),
};
/*static constexpr jammer_range_t range_presets[] = {
// GSM900 Orange
{ true, 935000000, 945000000 }, // BW:10M
// GSM1800 Orange
{ true, 1808000000, 1832000000 }, // BW:24M
// GSM900 SFR
{ true, 950000000, 960000000 }, // BW:10M
// GSM1800 SFR
{ true, 1832000000, 1853000000 }, // BW:21M
// GSM900 Bouygues
{ true, 925000000, 935000000 }, // BW:10M
// GSM1800 Bouygues
{ true, 1858000000, 1880000000 }, // BW:22M
// GSM900 Free
{ true, 945000000, 950000000 }, // BW:5M
// GSM-R
{ true, 921000000, 925000000 }, // BW:4M
// DECT
{ true, 1880000000, 1900000000 }, // BW: 20MHz
// PMV AFSK
{ true, 162930000, 162970000 }, // BW: 40kHz
// ISM 433
{ true, 433050000, 434790000 }, // Center: 433.92MHz BW: 0.2%
// ISM 868
{ true, 868000000, 868200000 }, // Center: 868.2MHz BW: 40kHz
// GPS L1
{ true, 1575420000 - 500000, 1575420000 + 500000 }, // BW: 1MHz
// GPS L2
{ true, 1227600000 - 1000000, 1227600000 + 1000000 }, // BW: 2MHz
// WLAN 2.4G CH1
{ true, 2412000000 - 11000000, 2412000000 + 11000000}, // BW: 22MHz
// WLAN 2.4G CH2
{ true, 2417000000 - 11000000, 2417000000 + 11000000}, // BW: 22MHz
// WLAN 2.4G CH3
{ true, 2422000000 - 11000000, 2422000000 + 11000000}, // BW: 22MHz
// WLAN 2.4G CH4
{ true, 2427000000 - 11000000, 2427000000 + 11000000}, // BW: 22MHz
// WLAN 2.4G CH5
{ true, 2432000000 - 11000000, 2432000000 + 11000000}, // BW: 22MHz
// WLAN 2.4G CH6
{ true, 2437000000 - 11000000, 2437000000 + 11000000}, // BW: 22MHz
// WLAN 2.4G CH7
{ true, 2442000000 - 11000000, 2442000000 + 11000000}, // BW: 22MHz
// WLAN 2.4G CH8
{ true, 2447000000 - 11000000, 2447000000 + 11000000}, // BW: 22MHz
// WLAN 2.4G CH9
{ true, 2452000000 - 11000000, 2452000000 + 11000000}, // BW: 22MHz
// WLAN 2.4G CH10
{ true, 2457000000 - 11000000, 2457000000 + 11000000}, // BW: 22MHz
// WLAN 2.4G CH11
{ true, 2462000000 - 11000000, 2462000000 + 11000000}, // BW: 22MHz
// WLAN 2.4G CH12
{ true, 2467000000 - 11000000, 2467000000 + 11000000}, // BW: 22MHz
// WLAN 2.4G CH13
{ true, 2472000000 - 11000000, 2472000000 + 11000000}, // BW: 22MHz
};*/
Labels labels {
{ { 2 * 8, 9 * 8 + 4 }, "Start", Color::light_grey() },
{ { 23 * 8, 9 * 8 + 4 }, "Stop", Color::light_grey() },
{ { 12 * 8, 6 * 8 }, "Center", Color::light_grey() },
{ { 12 * 8 + 4, 14 * 8 }, "Width", Color::light_grey() }
{ { 2 * 8, 8 * 8 + 4 }, "Start", Color::light_grey() },
{ { 23 * 8, 8 * 8 + 4 }, "Stop", Color::light_grey() },
{ { 12 * 8, 5 * 8 - 4}, "Center", Color::light_grey() },
{ { 12 * 8 + 4, 13 * 8 }, "Width", Color::light_grey() }
};
Checkbox check_enabled {
@@ -143,19 +77,19 @@ private:
};
Button button_start {
{ 0 * 8, 6 * 16, 11 * 8, 28 },
{ 0 * 8, 11 * 8, 11 * 8, 28 },
""
};
Button button_stop {
{ 19 * 8, 6 * 16, 11 * 8, 28 },
{ 19 * 8, 11 * 8, 11 * 8, 28 },
""
};
Button button_center {
{ 76, 4 * 16, 11 * 8, 28 },
{ 76, 4 * 15 - 4, 11 * 8, 28 },
""
};
Button button_width {
{ 76, 8 * 16, 11 * 8, 28 },
{ 76, 8 * 15, 11 * 8, 28 },
""
};
};
@@ -178,12 +112,17 @@ private:
NavigationView& nav_;
void start_tx();
void on_timer();
void stop_tx();
void set_jammer_channel(uint32_t i, uint32_t width, uint64_t center, uint32_t duration);
void on_retune(const rf::Frequency freq, const uint32_t range);
JammerChannel * jammer_channels = (JammerChannel*)shared_memory.bb_data.data;
bool jamming { false };
bool cooling { false }; //euquiq: Indicates jammer in cooldown
uint16_t seconds = 0; //euquiq: seconds counter for toggling tx / cooldown
int16_t mscounter = 0; //euquiq: Internal ms counter for do_timer()
lfsr_word_t lfsr_v = 1; //euquiq: Used to generate "random" Jitter
static constexpr Style style_val {
.font = font::fixed_8x16,
@@ -209,13 +148,19 @@ private:
};
Labels labels {
{ { 3 * 8, 12 * 16 }, "Type:", Color::light_grey() },
{ { 2 * 8, 13 * 16 }, "Speed:", Color::light_grey() },
{ { 5 * 8, 14 * 16 }, "Hop:", Color::light_grey() }
{ { 2 * 8, 23 * 8 }, "Type:", Color::light_grey() },
{ { 1 * 8, 25 * 8 }, "Speed:", Color::light_grey() },
{ { 3 * 8, 27 * 8 }, "Hop:", Color::light_grey() },
{ { 4 * 8, 29 * 8 }, "TX:", Color::light_grey() },
{ { 1 * 8, 31 * 8 }, "Sle3p:", Color::light_grey() }, //euquiq: Token of appreciation to TheSle3p, which made this ehnancement a reality with his bounty.
{ { 0 * 8, 33 * 8 }, "Jitter:", Color::light_grey() }, //Maybe the repository curator can keep the "mystype" for some versions.
{ { 11 * 8, 29 * 8 }, "Secs.", Color::light_grey() },
{ { 11 * 8, 31 * 8 }, "Secs.", Color::light_grey() },
{ { 11 * 8, 33 * 8 }, "/60", Color::light_grey() }
};
OptionsField options_type {
{ 9 * 8, 12 * 16 },
{ 7 * 8, 23 * 8 },
8,
{
{ "Rand FSK", 0 },
@@ -226,16 +171,16 @@ private:
};
Text text_range_number {
{ 22 * 8, 12 * 16, 2 * 8, 16 },
{ 16 * 8, 23 * 8, 2 * 8, 16 },
"--"
};
Text text_range_total {
{ 24 * 8, 12 * 16, 3 * 8, 16 },
{ 18 * 8, 23 * 8, 3 * 8, 16 },
"/--"
};
OptionsField options_speed {
{ 9 * 8, 13 * 16 },
{ 7 * 8, 25 * 8 },
6,
{
{ "10Hz ", 10 },
@@ -247,7 +192,7 @@ private:
};
OptionsField options_hop {
{ 9 * 8, 14 * 16 },
{ 7 * 8, 27 * 8 },
5,
{
{ "10ms ", 1 },
@@ -259,9 +204,33 @@ private:
{ "10s ", 1000 }
}
};
NumberField field_timetx {
{ 7 * 8, 29 * 8 },
3,
{ 1, 180 },
1,
' ',
};
NumberField field_timepause {
{ 8 * 8, 31 * 8 },
2,
{ 1, 60 },
1,
' ',
};
NumberField field_jitter {
{ 8 * 8, 33 * 8 },
2,
{ 1, 60 },
1,
' ',
};
Button button_transmit {
{ 9 * 8, 16 * 16, 96, 48 },
{ 148, 212, 80, 80},
"START"
};
@@ -272,6 +241,14 @@ private:
this->on_retune(message->freq, message->range);
}
};
MessageHandlerRegistration message_handler_frame_sync {
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->on_timer();
}
};
};
} /* namespace ui */
@@ -0,0 +1,336 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2020 euquiq
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_looking_glass_app.hpp"
using namespace portapack;
namespace ui
{
void GlassView::focus() {
field_marker.focus();
}
GlassView::~GlassView() {
receiver_model.set_sampling_rate(3072000); // Just a hack to avoid hanging other apps
receiver_model.disable();
baseband::shutdown();
}
void GlassView::on_lna_changed(int32_t v_db) {
receiver_model.set_lna(v_db);
}
void GlassView::on_vga_changed(int32_t v_db) {
receiver_model.set_vga(v_db);
}
void GlassView::add_spectrum_pixel(Color color)
{
spectrum_row[pixel_index++] = color;
if (pixel_index == 240) //got an entire waterfall line
{
const auto draw_y = display.scroll(1); //Scroll 1 pixel down
display.draw_pixels( {{0, draw_y}, {240, 1}}, spectrum_row); //new line at top
pixel_index = 0; //Start New cascade line
}
}
//Apparently, the spectrum object returns an array of 256 bins
//Each having the radio signal power for it's corresponding frequency slot
void GlassView::on_channel_spectrum(const ChannelSpectrum &spectrum)
{
baseband::spectrum_streaming_stop();
// Convert bins of this spectrum slice into a representative max_power and when enough, into pixels
// Spectrum.db has 256 bins. Center 12 bins are ignored (DC spike is blanked) Leftmost and rightmost 2 bins are ignored
// All things said and done, we actually need 240 of those bins:
for(uint8_t bin = 0; bin < 240; bin++)
{
if (bin < 120) {
if (spectrum.db[134 + bin] > max_power)
max_power = spectrum.db[134 + bin];
}
else
{
if (spectrum.db[bin - 118] > max_power)
max_power = spectrum.db[bin - 118];
}
bins_Hz_size += each_bin_size; //add this bin Hz count into the "pixel fulfilled bag of Hz"
if (bins_Hz_size >= marker_pixel_step) //new pixel fullfilled
{
if (min_color_power < max_power)
add_spectrum_pixel(spectrum_rgb3_lut[max_power]); //Pixel will represent max_power
else
add_spectrum_pixel(0); //Filtered out, show black
max_power = 0;
if (!pixel_index) //Received indication that a waterfall line has been completed
{
bins_Hz_size = 0; //Since this is an entire pixel line, we don't carry "Pixels into next bin"
break;
} else {
bins_Hz_size -= marker_pixel_step; //reset bins size, but carrying the eventual excess Hz into next pixel
}
}
}
if (pixel_index)
f_center += LOOKING_GLASS_SLICE_WIDTH; //Move into the next bandwidth slice NOTE: spectrum.sampling_rate = LOOKING_GLASS_SLICE_WIDTH
else
f_center = f_center_ini; //Start a new sweep
receiver_model.set_tuning_frequency(f_center); //tune rx for this slice
baseband::spectrum_streaming_start(); //Do the RX
}
void GlassView::on_hide()
{
baseband::spectrum_streaming_stop();
display.scroll_disable();
}
void GlassView::on_show()
{
display.scroll_set_area( 109, 319); //Restart scroll on the correct coordinates
baseband::spectrum_streaming_start();
}
void GlassView::on_range_changed()
{
f_min = field_frequency_min.value();
f_max = field_frequency_max.value();
search_span = f_max - f_min;
field_marker.set_range(f_min, f_max); //Move the marker between range
field_marker.set_value(f_min + (search_span / 2)); //Put MARKER AT MIDDLE RANGE
text_range.set(to_string_dec_uint(search_span));
f_min = (f_min)*MHZ_DIV; //Transpose into full frequency realm
f_max = (f_max)*MHZ_DIV;
search_span = search_span * MHZ_DIV;
marker_pixel_step = search_span / 240; //Each pixel value in Hz
text_marker_pm.set(to_string_dec_uint((marker_pixel_step / X2_MHZ_DIV) + 1)); // Give idea of +/- marker precision
int32_t marker_step = marker_pixel_step / MHZ_DIV;
if (!marker_step)
field_marker.set_step(1); //in case selected range is less than 240 (pixels)
else
field_marker.set_step(marker_step); //step needs to be a pixel wide.
f_center_ini = f_min + (LOOKING_GLASS_SLICE_WIDTH / 2); //Initial center frequency for sweep
f_center_ini += LOOKING_GLASS_SLICE_WIDTH; //euquiq: Why do I need to move the center ???!!! (shift needed for marker accuracy)
PlotMarker(field_marker.value()); //Refresh marker on screen
f_center = f_center_ini; //Reset sweep into first slice
pixel_index = 0; //reset pixel counter
max_power = 0;
bins_Hz_size = 0; //reset amount of Hz filled up by pixels
baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, field_trigger.value());
receiver_model.set_tuning_frequency(f_center_ini); //tune rx for this slice
}
void GlassView::PlotMarker(rf::Frequency fpos)
{
int64_t pos = fpos * MHZ_DIV;
pos -= f_min;
pos = pos / marker_pixel_step; //Real pixel
portapack::display.fill_rectangle({0, 100, 240, 8}, Color::black()); //Clear old marker and whole marker rectangle btw
portapack::display.fill_rectangle({(int16_t)pos - 2, 100, 5, 3}, Color::red()); //Red marker middle
portapack::display.fill_rectangle({(int16_t)pos - 1, 103, 3, 3}, Color::red()); //Red marker middle
portapack::display.fill_rectangle({(int16_t)pos, 106, 1, 2}, Color::red()); //Red marker middle
}
GlassView::GlassView(
NavigationView &nav) : nav_(nav)
{
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
add_children({&labels,
&field_frequency_min,
&field_frequency_max,
&field_lna,
&field_vga,
&text_range,
&filter_config,
&field_rf_amp,
&range_presets,
&field_marker,
&text_marker_pm,
&field_trigger
});
load_Presets(); //Load available presets from TXT files (or default)
field_frequency_min.set_value(presets_db[0].min); //Defaults to first preset
field_frequency_min.on_change = [this](int32_t v) {
if (v >= field_frequency_max.value())
field_frequency_max.set_value(v + 240);
this->on_range_changed();
};
field_frequency_max.set_value(presets_db[0].max); //Defaults to first preset
field_frequency_max.on_change = [this](int32_t v) {
if (v <= field_frequency_min.value())
field_frequency_min.set_value(v - 240);
this->on_range_changed();
};
field_lna.set_value(receiver_model.lna());
field_lna.on_change = [this](int32_t v) {
this->on_lna_changed(v);
};
field_vga.set_value(receiver_model.vga());
field_vga.on_change = [this](int32_t v_db) {
this->on_vga_changed(v_db);
};
filter_config.set_selected_index(0);
filter_config.on_change = [this](size_t n, OptionsField::value_t v) {
(void)n;
min_color_power = v;
};
range_presets.on_change = [this](size_t n, OptionsField::value_t v) {
(void)n;
field_frequency_min.set_value(presets_db[v].min,false);
field_frequency_max.set_value(presets_db[v].max,false);
this->on_range_changed();
};
field_marker.on_change = [this](int32_t v) {
PlotMarker(v); //Refresh marker on screen
};
field_marker.on_select = [this](NumberField&) {
f_center = field_marker.value();
f_center = f_center * MHZ_DIV;
receiver_model.set_tuning_frequency(f_center); //Center tune rx in marker freq.
receiver_model.set_frequency_step(MHZ_DIV); // Preset a 1 MHz frequency step into RX -> AUDIO
nav_.pop();
nav_.push<AnalogAudioView>(); //Jump into audio view
};
field_trigger.set_value(32); //Defaults to 32, as normal triggering resolution
field_trigger.on_change = [this](int32_t v) {
baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, v);
};
display.scroll_set_area( 109, 319);
baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, field_trigger.value()); //trigger:
// Discord User jteich: WidebandSpectrum::on_message to set the trigger value. In WidebandSpectrum::execute ,
// it keeps adding the output of the fft to the buffer until "trigger" number of calls are made,
//at which time it pushes the buffer up with channel_spectrum.feed
on_range_changed();
receiver_model.set_modulation(ReceiverModel::Mode::SpectrumAnalysis);
receiver_model.set_sampling_rate(LOOKING_GLASS_SLICE_WIDTH); //20mhz
receiver_model.set_baseband_bandwidth(LOOKING_GLASS_SLICE_WIDTH); // possible values: 1.75/2.5/3.5/5/5.5/6/7/8/9/10/12/14/15/20/24/28MHz
receiver_model.set_squelch_level(0);
receiver_model.enable();
}
void GlassView::load_Presets() {
File presets_file; //LOAD /WHIPCALC/ANTENNAS.TXT from microSD
auto result = presets_file.open("LOOKINGGLASS/PRESETS.TXT");
presets_db.clear(); //Start with fresh db
if (result.is_valid()) {
presets_Default(); //There is no txt, store a default range
} else {
std::string line; //There is a txt file
char one_char[1]; //Read it char by char
for (size_t pointer=0; pointer < presets_file.size();pointer++) {
presets_file.seek(pointer);
presets_file.read(one_char, 1);
if ((int)one_char[0] > 31) { //ascii space upwards
line += one_char[0]; //Add it to the textline
}
else if (one_char[0] == '\n') { //New Line
txtline_process(line); //make sense of this textline
line.clear(); //Ready for next textline
}
}
if (line.length() > 0) txtline_process(line); //Last line had no newline at end ?
if (!presets_db.size()) presets_Default(); //no antenna on txt, use default
}
populate_Presets();
}
void GlassView::txtline_process(std::string& line)
{
if (line.find("#") != std::string::npos) return; //Line is just a comment
size_t comma = line.find(","); //Get first comma position
if (comma == std::string::npos) return; //No comma at all
size_t previous = 0;
preset_entry new_preset;
new_preset.min = std::stoi(line.substr(0,comma));
if (!new_preset.min) return; //No frequency!
previous = comma + 1;
comma = line.find(",",previous); //Search for next delimiter
if (comma == std::string::npos) return; //No comma at all
new_preset.max = std::stoi(line.substr(previous,comma - previous));
if (!new_preset.max) return; //No frequency!
new_preset.label = line.substr(comma + 1);
if (new_preset.label.size() == 0) return; //No label ?
presets_db.push_back(new_preset); //Add this preset.
}
void GlassView::populate_Presets()
{
using option_t = std::pair<std::string, int32_t>;
using options_t = std::vector<option_t>;
options_t entries;
for (preset_entry preset : presets_db)
{ //go thru all available presets
entries.emplace_back(preset.label,entries.size());
}
range_presets.set_options(entries);
}
void GlassView::presets_Default()
{
presets_db.clear();
presets_db.push_back({2320, 2560, "DEFAULT WIFI 2.4GHz"});
}
}
@@ -0,0 +1,187 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2020 euquiq
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "portapack.hpp"
#include "baseband_api.hpp"
#include "receiver_model.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "string_format.hpp"
#include "analog_audio_app.hpp"
#include "spectrum_color_lut.hpp"
namespace ui
{
#define LOOKING_GLASS_SLICE_WIDTH 20000000 // Each slice bandwidth 20 MHz
#define MHZ_DIV 1000000
#define X2_MHZ_DIV 2000000
class GlassView : public View
{
public:
GlassView(NavigationView &nav);
GlassView( const GlassView &);
GlassView& operator=(const GlassView &nav);
~GlassView();
std::string title() const override { return "Looking Glass"; };
void on_show() override;
void on_hide() override;
void focus() override;
private:
NavigationView& nav_;
struct preset_entry
{
rf::Frequency min{};
rf::Frequency max{};
std::string label{};
};
std::vector<preset_entry> presets_db{};
void on_channel_spectrum(const ChannelSpectrum& spectrum);
void do_timers();
void on_range_changed();
void on_lna_changed(int32_t v_db);
void on_vga_changed(int32_t v_db);
void add_spectrum_pixel(Color color);
void PlotMarker(rf::Frequency pos);
void load_Presets();
void txtline_process(std::string& line);
void populate_Presets();
void presets_Default();
rf::Frequency f_min { 0 }, f_max { 0 };
rf::Frequency search_span { 0 };
rf::Frequency f_center { 0 };
rf::Frequency f_center_ini { 0 };
rf::Frequency marker_pixel_step { 0 };
rf::Frequency each_bin_size { LOOKING_GLASS_SLICE_WIDTH / 240 };
rf::Frequency bins_Hz_size { 0 };
uint8_t min_color_power { 0 };
uint32_t pixel_index { 0 };
std::array<Color, 240> spectrum_row = { 0 };
ChannelSpectrumFIFO* fifo { nullptr };
uint8_t max_power = 0;
Labels labels{
{{0, 0}, "MIN: MAX: LNA VGA ", Color::light_grey()},
{{0, 1 * 16}, " RANGE: FILTER: AMP:", Color::light_grey()},
{{0, 2 * 16}, "PRESET:", Color::light_grey()},
{{0, 3 * 16}, "MARKER: MHz +/- MHz", Color::light_grey()},
{{0, 4 * 16}, "RESOLUTION: (fft trigger)", Color::light_grey()}
};
NumberField field_frequency_min {
{ 4 * 8, 0 * 16 },
4,
{ 0, 6960 },
240,
' '
};
NumberField field_frequency_max {
{ 13 * 8, 0 * 16 },
4,
{ 240, 7200 },
240,
' '
};
LNAGainField field_lna {
{ 21 * 8, 0 * 16 }
};
VGAGainField field_vga {
{ 27 * 8, 0 * 16 }
};
Text text_range{
{7 * 8, 1 * 16, 4 * 8, 16},
""};
OptionsField filter_config{
{19 * 8, 1 * 16},
4,
{
{"OFF ", 0},
{"MID ", 118}, //85 +25 (110) + a bit more to kill all blue
{"HIGH", 202}, //168 + 25 (193)
}};
RFAmpField field_rf_amp{
{29 * 8, 1 * 16}};
OptionsField range_presets{
{7 * 8, 2 * 16},
20,
{
{" NONE (WIFI 2.4GHz)", 0},
}};
NumberField field_marker{
{7 * 8, 3 * 16},
4,
{0, 7200},
25,
' '};
Text text_marker_pm{
{20 * 8, 3 * 16, 2 * 8, 16},
""};
NumberField field_trigger{
{11 * 8, 4 * 16},
3,
{2, 128},
2,
' '};
MessageHandlerRegistration message_handler_spectrum_config {
Message::ID::ChannelSpectrumConfig,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
this->fifo = message.fifo;
}
};
MessageHandlerRegistration message_handler_frame_sync {
Message::ID::DisplayFrameSync,
[this](const Message* const) {
if( this->fifo ) {
ChannelSpectrum channel_spectrum;
while( fifo->out(channel_spectrum) ) {
this->on_channel_spectrum(channel_spectrum);
}
}
}
};
};
}
+77 -7
View File
@@ -65,8 +65,13 @@ void MicTXView::configure_baseband() {
sampling_rate / 20, // Update vu-meter at 20Hz
transmitting ? transmitter_model.channel_bandwidth() : 0,
mic_gain,
TONES_F2D(tone_key_frequency(tone_key_index), sampling_rate)
TONES_F2D(tone_key_frequency(tone_key_index), sampling_rate),
enable_am,
enable_dsb,
enable_usb,
enable_lsb
);
}
void MicTXView::set_tx(bool enable) {
@@ -143,8 +148,20 @@ void MicTXView::rxaudio(bool is_on) {
if (is_on) {
audio::input::stop();
baseband::shutdown();
baseband::run_image(portapack::spi_flash::image_tag_nfm_audio);
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
if (enable_am || enable_usb || enable_lsb || enable_dsb) {
baseband::run_image(portapack::spi_flash::image_tag_am_audio);
receiver_model.set_modulation(ReceiverModel::Mode::AMAudio);
if (options_mode.selected_index() < 4)
receiver_model.set_am_configuration(options_mode.selected_index() - 1);
else
receiver_model.set_am_configuration(0);
}
else {
baseband::run_image(portapack::spi_flash::image_tag_nfm_audio);
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
}
receiver_model.set_sampling_rate(3072000);
receiver_model.set_baseband_bandwidth(1750000);
// receiver_model.set_tuning_frequency(field_frequency.value()); //probably this too can be commented out.
@@ -155,15 +172,15 @@ void MicTXView::rxaudio(bool is_on) {
receiver_model.enable();
audio::output::start();
} else { //These incredibly convoluted steps are required for the vumeter to reappear when stopping RX.
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); //This fixes something with AM RX...
receiver_model.disable();
baseband::shutdown();
baseband::run_image(portapack::spi_flash::image_tag_mic_tx);
audio::input::start();
// transmitter_model.enable();
audio::output::stop();
audio::input::start();
portapack::pin_i2s0_rx_sda.mode(3);
// transmitting = false;
configure_baseband();
// transmitter_model.disable();
}
}
@@ -198,11 +215,13 @@ MicTXView::MicTXView(
&field_bw,
&field_rfgain,
&field_rfamp,
&options_mode,
&field_frequency,
&options_tone_key,
&check_rogerbeep,
&check_rxactive,
&field_volume,
&field_rxbw,
&field_squelch,
&field_rxfrequency,
&field_rxlna,
@@ -262,6 +281,42 @@ MicTXView::MicTXView(
};
field_rfamp.set_value(rf_amp ? 14 : 0);
options_mode.on_change = [this](size_t, int32_t v) {
enable_am = false;
enable_usb = false;
enable_lsb = false;
enable_dsb = false;
switch(v) {
case 0:
enable_am = false;
enable_usb = false;
enable_lsb = false;
enable_dsb = false;
field_bw.set_value(transmitter_model.channel_bandwidth() / 1000);
//if (rx_enabled)
rxaudio(rx_enabled); //Update now if we have RX audio on
break;
case 1:
enable_am = true;
rxaudio(rx_enabled); //Update now if we have RX audio on
break;
case 2:
enable_usb = true;
rxaudio(rx_enabled); //Update now if we have RX audio on
break;
case 3:
enable_lsb = true;
rxaudio(rx_enabled); //Update now if we have RX audio on
break;
case 4:
enable_dsb = true;
rxaudio(rx_enabled); //Update now if we have RX audio on
break;
}
//configure_baseband();
};
/*
check_va.on_select = [this](Checkbox&, bool v) {
va_enabled = v;
@@ -331,6 +386,21 @@ MicTXView::MicTXView(
field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99);
field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); };
field_rxbw.on_change = [this](size_t, int32_t v) {
switch(v) {
case 0:
receiver_model.set_nbfm_configuration(0);
break;
case 1:
receiver_model.set_nbfm_configuration(1);
break;
case 2:
receiver_model.set_nbfm_configuration(2);
break;
}
};
field_rxbw.set_selected_index(2);
field_squelch.on_change = [this](int32_t v) {
receiver_model.set_squelch_level(100 - v);
};
+31 -1
View File
@@ -99,19 +99,27 @@ private:
int32_t focused_ui { 2 };
bool button_touch { false };
//AM TX Stuff
bool enable_am { false };
bool enable_dsb { false };
bool enable_usb { false };
bool enable_lsb { false };
Labels labels {
{ { 3 * 8, 1 * 8 }, "MIC. GAIN:", Color::light_grey() },
{ { 3 * 8, 3 * 8 }, "F:", Color::light_grey() },
{ { 15 * 8, 3 * 8 }, "BW: kHz", Color::light_grey() },
{ { 15 * 8, 3 * 8 }, "BW: FM kHz", Color::light_grey() },
{ { 3 * 8, 5 * 8 }, "GAIN:", Color::light_grey() },
{ {11 * 8, 5 * 8 }, "Amp:", Color::light_grey() },
{ { 18 * 8, (5 * 8) }, "Mode:", Color::light_grey() },
{ { 3 * 8, 8 * 8 }, "TX Activation:", Color::light_grey() },
{ { 4 * 8, 10 * 8 }, "LVL:", Color::light_grey() },
{ {12 * 8, 10 * 8 }, "ATT:", Color::light_grey() },
{ {20 * 8, 10 * 8 }, "DEC:", Color::light_grey() },
{ { 4 * 8, ( 13 * 8 ) - 2 }, "TONE KEY:", Color::light_grey() },
{ { 9 * 8, 23 * 8 }, "VOL:", Color::light_grey() },
{ {17 * 8, 23 * 8 }, "FM RXBW:", Color::light_grey() },
{ {17 * 8, 25 * 8 }, "SQ:", Color::light_grey() },
{ { 5 * 8, 25 * 8 }, "F:", Color::light_grey() },
{ { 5 * 8, 27 * 8 }, "LNA:", Color::light_grey()},
@@ -162,6 +170,18 @@ private:
14,
' '
};
OptionsField options_mode {
{ 24 * 8, 5 * 8 },
3,
{
{ "FM", 0 },
{ "AM", 1 },
{ "USB", 2 },
{ "LSB", 3 },
{ "DSB", 4 }
}
};
/*
Checkbox check_va {
{ 3 * 8, (10 * 8) - 4 },
@@ -231,6 +251,16 @@ private:
' ',
};
OptionsField field_rxbw {
{ 25 * 8, 23 * 8},
3,
{
{"8k5", 0},
{"11k", 1},
{"16k", 2}
}
};
NumberField field_squelch {
{ 20 * 8, 25 * 8 },
2,
+64 -7
View File
@@ -69,6 +69,25 @@ static msg_t ookthread_fn(void * arg) {
return 0;
}
static msg_t loopthread_fn(void * arg) {
MorseView * arg_c = (MorseView*)arg;
uint32_t wait = arg_c->loop;
chRegSetThreadName("loopthread");
for (uint32_t i = 0; i < wait; i++) {
if (chThdShouldTerminate()) break;
arg_c->on_loop_progress(i, false);
chThdSleepMilliseconds(1000);
}
arg_c->on_loop_progress(0, true);
chThdExit(0);
return 0;
}
void MorseView::on_set_text(NavigationView& nav) {
text_prompt(nav, buffer, 28);
}
@@ -115,9 +134,9 @@ bool MorseView::start_tx() {
void MorseView::update_tx_duration() {
uint32_t duration_ms;
time_unit_ms = field_time_unit.value();
time_unit_ms = 1200 / field_speed.value();
symbol_count = morse_encode(message, time_unit_ms, field_tone.value(), &time_units);
if (symbol_count) {
duration_ms = time_units * time_unit_ms;
text_tx_duration.set(to_string_dec_uint(duration_ms / 1000) + "." + to_string_dec_uint((duration_ms / 100) % 10, 1) + "s ");
@@ -130,7 +149,28 @@ void MorseView::on_tx_progress(const uint32_t progress, const bool done) {
if (done) {
transmitter_model.disable();
progressbar.set_value(0);
tx_view.set_transmitting(false);
if (loop && run) {
text_tx_duration.set("wait");
progressbar.set_max(loop);
progressbar.set_value(0);
if (loopthread) {
chThdRelease(loopthread);
loopthread = nullptr;
}
loopthread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO, loopthread_fn, this);
} else {
tx_view.set_transmitting(false);
}
} else
progressbar.set_value(progress);
}
void MorseView::on_loop_progress(const uint32_t progress, const bool done) {
if (done) {
start_tx();
} else
progressbar.set_value(progress);
}
@@ -152,9 +192,10 @@ MorseView::MorseView(
&labels,
&checkbox_foxhunt,
&options_foxhunt,
&field_time_unit,
&field_speed,
&field_tone,
&options_modulation,
&options_loop,
&text_tx_duration,
&text_message,
&button_message,
@@ -163,9 +204,10 @@ MorseView::MorseView(
});
// Default settings
field_time_unit.set_value(50); // 50ms unit
field_speed.set_value(15); // 15wps
field_tone.set_value(700); // 700Hz FM tone
options_modulation.set_selected_index(0); // CW mode
options_loop.set_selected_index(0); // Off
checkbox_foxhunt.on_select = [this](Checkbox&, bool value) {
foxhunt_mode = value;
@@ -182,8 +224,13 @@ MorseView::MorseView(
options_modulation.on_change = [this](size_t i, int32_t) {
modulation = (modulation_t)i;
};
options_loop.on_change = [this](size_t i, uint32_t n) {
(void)i; //avoid unused warning
loop = n;
};
field_time_unit.on_change = [this](int32_t) {
field_speed.on_change = [this](int32_t) {
update_tx_duration();
};
@@ -199,12 +246,22 @@ MorseView::MorseView(
};
tx_view.on_start = [this]() {
if (start_tx())
if (start_tx()) {
run = true;
tx_view.set_transmitting(true);
}
};
tx_view.on_stop = [this]() {
run = false;
if (ookthread) chThdTerminate(ookthread);
if (loopthread) {
chThdTerminate(loopthread);
chThdWait(loopthread);
loopthread = nullptr;
}
transmitter_model.disable();
baseband::kill_tone();
tx_view.set_transmitting(false);
+23 -4
View File
@@ -54,11 +54,13 @@ public:
void paint(Painter& painter) override;
void on_tx_progress(const uint32_t progress, const bool done);
void on_loop_progress(const uint32_t progress, const bool done);
std::string title() const override { return "Morse TX"; };
uint32_t time_unit_ms { 0 };
size_t symbol_count { 0 };
uint32_t loop { 0 };
private:
NavigationView& nav_;
std::string buffer { "PORTAPACK" };
@@ -77,12 +79,15 @@ private:
void set_foxhunt(size_t i);
Thread * ookthread { nullptr };
Thread * loopthread { nullptr };
bool foxhunt_mode { false };
bool run { false };
Labels labels {
{ { 4 * 8, 6 * 8 }, "Time unit: ms", Color::light_grey() },
{ { 4 * 8, 6 * 8 }, "Speed: wps", Color::light_grey() },
{ { 4 * 8, 8 * 8 }, "Tone: Hz", Color::light_grey() },
{ { 4 * 8, 10 * 8 }, "Modulation:", Color::light_grey() },
{ { 4 * 8, 12 * 8 }, "Loop:", Color::light_grey() },
{ { 1 * 8, 25 * 8 }, "TX will last", Color::light_grey() }
};
@@ -109,10 +114,10 @@ private:
}
};
NumberField field_time_unit {
{ 14 * 8, 6 * 8 },
NumberField field_speed {
{ 10 * 8, 6 * 8 },
3,
{ 10, 999 },
{ 10, 45 },
1,
' '
};
@@ -133,6 +138,20 @@ private:
{ "FM", 1 }
}
};
OptionsField options_loop {
{ 9 * 8, 12 * 8 },
6,
{
{ "Off", 0 },
{ "5 sec", 5 },
{ "10 sec", 10 },
{ "30 sec", 30 },
{ "1 min", 60 },
{ "3 min", 180 },
{ "5 min", 300 }
}
};
Text text_tx_duration {
{ 14 * 8, 25 * 8, 4 * 8, 16 },
+62 -17
View File
@@ -21,6 +21,7 @@
*/
#include "ui_scanner.hpp"
#include "ui_fileman.hpp"
using namespace portapack;
@@ -171,7 +172,7 @@ void ScannerView::show_max() { //show total number of freqs to scan
ScannerView::ScannerView(
NavigationView& nav
) : nav_ { nav }
) : nav_ { nav } , loaded_file_name { "SCANNER" }
{
add_children({
&labels,
@@ -182,6 +183,7 @@ ScannerView::ScannerView(
&field_bw,
&field_squelch,
&field_wait,
&button_load,
&rssi,
&text_cycle,
&text_max,
@@ -211,6 +213,25 @@ ScannerView::ScannerView(
frequency_range.max = stored_freq + 1000000;
button_manual_end.set_text(to_string_short_freq(frequency_range.max));
button_load.on_select = [this, &nav](Button&) {
// load txt files from the FREQMAN folder
auto open_view = nav.push<FileLoadView>(".TXT");
open_view->on_changed = [this](std::filesystem::path new_file_path) {
std::string dir_filter = "FREQMAN/";
std::string str_file_path = new_file_path.string();
if (str_file_path.find(dir_filter) != string::npos) { // assert file from the FREQMAN folder
scan_pause();
// get the filename without txt extension so we can use load_freqman_file fcn
std::string str_file_name = new_file_path.stem().string();
frequency_file_load(str_file_name, true);
} else {
nav_.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired.");
}
};
};
button_manual_start.on_select = [this, &nav](Button& button) {
auto new_view = nav_.push<FrequencyKeypadView>(frequency_range.min);
new_view->on_changed = [this, &button](rf::Frequency f) {
@@ -314,7 +335,8 @@ ScannerView::ScannerView(
button_add.on_select = [this](Button&) { //frequency_list[current_index]
File scanner_file;
auto result = scanner_file.open("FREQMAN/SCANNER.TXT"); //First search if freq is already in txt
std::string freq_file_path = "FREQMAN/" + loaded_file_name + ".TXT";
auto result = scanner_file.open(freq_file_path); //First search if freq is already in txt
if (!result.is_valid()) {
std::string frequency_to_add = "f="
+ to_string_dec_uint(frequency_list[current_index] / 1000)
@@ -342,12 +364,12 @@ ScannerView::ScannerView(
big_display.set(frequency_list[current_index]); //After showing an error
}
else {
auto result = scanner_file.append("FREQMAN/SCANNER.TXT"); //Second: append if it is not there
scanner_file.append(freq_file_path); //Second: append if it is not there
scanner_file.write_line(frequency_to_add + ",d=ADD FQ");
}
} else
{
nav_.display_modal("Error", "Cannot open SCANNER.TXT\nfor appending freq.");
nav_.display_modal("Error", "Cannot open " + loaded_file_name + ".TXT\nfor appending freq.");
big_display.set(frequency_list[current_index]); //After showing an error
}
};
@@ -357,22 +379,42 @@ ScannerView::ScannerView(
field_squelch.on_change = [this](int32_t v) { squelch = v; }; field_squelch.set_value(-10);
field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99);
field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); };
// LEARN FREQUENCIES
std::string scanner_txt = "SCANNER";
if ( load_freqman_file(scanner_txt, database) ) {
frequency_file_load(scanner_txt);
}
void ScannerView::frequency_file_load(std::string file_name, bool stop_all_before) {
// stop everything running now if required
if (stop_all_before) {
scan_thread->stop();
frequency_list.clear(); // clear the existing frequency list (expected behavior)
description_list.clear();
def_step = step_mode.selected_index_value(); //Use def_step from manual selector
}
if ( load_freqman_file(file_name, database) ) {
loaded_file_name = file_name; // keep loaded filename in memory
for(auto& entry : database) { // READ LINE PER LINE
if (frequency_list.size() < MAX_DB_ENTRY) { //We got space!
if (entry.type == RANGE) { //RANGE
switch (entry.step) {
case AM_US: def_step = 10000; break ;
case AM_EUR:def_step = 9000; break ;
case NFM_1: def_step = 12500; break ;
case NFM_2: def_step = 6250; break ;
case FM_1: def_step = 100000; break ;
case FM_2: def_step = 50000; break ;
case N_1: def_step = 25000; break ;
case N_2: def_step = 250000; break ;
case AIRBAND:def_step= 8330; break ;
case AM_US: def_step = 10000; break ;
case AM_EUR:def_step = 9000; break ;
case NFM_1: def_step = 12500; break ;
case NFM_2: def_step = 6250; break ;
case FM_1: def_step = 100000; break ;
case FM_2: def_step = 50000; break ;
case N_1: def_step = 25000; break ;
case N_2: def_step = 250000; break ;
case AIRBAND: def_step= 8330; break ;
case ERROR_STEP:
case STEP_DEF:
default:
def_step = step_mode.selected_index_value(); //Use def_step from manual selector
break ;
}
frequency_list.push_back(entry.frequency_a); //Store starting freq and description
description_list.push_back("R" + to_string_short_freq(entry.frequency_a)
@@ -398,7 +440,8 @@ ScannerView::ScannerView(
}
else
{
desc_cycle.set(" NO SCANNER.TXT FILE ..." );
loaded_file_name = "SCANNER"; // back to the default frequency file
desc_cycle.set(" NO " + file_name + ".TXT FILE ..." );
}
audio::output::stop();
step_mode.set_by_value(def_step); //Impose the default step into the manual step selector
@@ -466,7 +509,9 @@ size_t ScannerView::change_mode(uint8_t new_mod) { //Before this, do a scan_thre
using option_t = std::pair<std::string, int32_t>;
using options_t = std::vector<option_t>;
options_t bw;
field_bw.on_change = [this](size_t n, OptionsField::value_t) { };
field_bw.on_change = [this](size_t n, OptionsField::value_t) {
(void)n; //avoid unused warning
};
switch (new_mod) {
case NFM: //bw 16k (2) default
@@ -518,4 +563,4 @@ void ScannerView::start_scan_thread() {
scan_thread = std::make_unique<ScannerThread>(frequency_list);
}
} /* namespace ui */
} /* namespace ui */
+7
View File
@@ -126,6 +126,7 @@ private:
void scan_pause();
void scan_resume();
void user_resume();
void frequency_file_load(std::string file_name, bool stop_all_before = false);
void on_statistics_update(const ChannelStatistics& statistics);
void on_headphone_volume_changed(int32_t v);
@@ -137,6 +138,7 @@ private:
uint32_t wait { 0 };
size_t def_step { 0 };
freqman_db database { };
std::string loaded_file_name;
uint32_t current_index { 0 };
bool userpause { false };
@@ -278,6 +280,11 @@ private:
"ADD FQ"
};
Button button_load {
{ 24 * 8, 3 * 16 - 8, 6 * 8, 22 },
"Load"
};
Button button_remove {
{ 168, (35 * 8) - 4, 72, 28 },
"DEL FQ"
+6 -4
View File
@@ -212,12 +212,14 @@ private:
true
};
OptionsField options_snap {
{ 17 * 8, 15 * 8 },
7,
{
{ 17 * 8, 15 * 8 }, // Position
7, // Length
{ // Options
{ "25kHz ", 25000 },
{ "12.5kHz", 12500 },
{ "8.33kHz", 8333 }
{ "8.33kHz", 8333 },
{ "2.5kHz", 2500 },
{ "500Hz", 500 }
}
};
+57 -2
View File
@@ -144,6 +144,10 @@ SetRadioView::SetRadioView(
}
add_children({
&check_clkout,
&field_clkout_freq,
&labels_clkout_khz,
&value_freq_step,
&labels_bias,
&check_bias,
&button_done,
@@ -151,11 +155,44 @@ SetRadioView::SetRadioView(
});
SetFrequencyCorrectionModel model {
static_cast<int8_t>(portapack::persistent_memory::correction_ppb() / 1000)
static_cast<int8_t>(portapack::persistent_memory::correction_ppb() / 1000) , 0
};
form_init(model);
check_clkout.set_value(portapack::persistent_memory::clkout_enabled());
check_clkout.on_select = [this](Checkbox&, bool v) {
clock_manager.enable_clock_output(v);
portapack::persistent_memory::set_clkout_enabled(v);
StatusRefreshMessage message { };
EventDispatcher::send_message(message);
};
field_clkout_freq.set_value(portapack::persistent_memory::clkout_freq());
value_freq_step.set_style(&style_text);
field_clkout_freq.on_select = [this](NumberField&) {
freq_step_khz++;
if(freq_step_khz > 3) {
freq_step_khz = 0;
}
switch(freq_step_khz) {
case 0:
value_freq_step.set(" |");
break;
case 1:
value_freq_step.set(" | ");
break;
case 2:
value_freq_step.set(" | ");
break;
case 3:
value_freq_step.set("| ");
break;
}
field_clkout_freq.set_step(pow(10, freq_step_khz));
};
check_bias.set_value(portapack::get_antenna_bias());
check_bias.on_select = [this](Checkbox&, bool v) {
portapack::set_antenna_bias(v);
@@ -166,6 +203,8 @@ SetRadioView::SetRadioView(
button_done.on_select = [this, &nav](Button&){
const auto model = this->form_collect();
portapack::persistent_memory::set_correction_ppb(model.ppm * 1000);
portapack::persistent_memory::set_clkout_freq(model.freq);
clock_manager.enable_clock_output(portapack::persistent_memory::clkout_enabled());
nav.pop();
};
}
@@ -181,6 +220,7 @@ void SetRadioView::form_init(const SetFrequencyCorrectionModel& model) {
SetFrequencyCorrectionModel SetRadioView::form_collect() {
return {
.ppm = static_cast<int8_t>(field_ppm.value()),
.freq = static_cast<uint32_t>(field_clkout_freq.value()),
};
}
@@ -245,15 +285,17 @@ SetUIView::SetUIView(NavigationView& nav) {
&checkbox_bloff,
&options_bloff,
&checkbox_showsplash,
&checkbox_showclock,
&options_clockformat,
&button_ok
});
checkbox_speaker.set_value(persistent_memory::config_speaker());
checkbox_showsplash.set_value(persistent_memory::config_splash());
checkbox_showclock.set_value(!persistent_memory::hide_clock());
//checkbox_login.set_value(persistent_memory::config_login());
uint32_t backlight_timer = persistent_memory::config_backlight_timer();
if (backlight_timer) {
checkbox_bloff.set_value(true);
options_bloff.set_by_value(backlight_timer);
@@ -261,6 +303,12 @@ SetUIView::SetUIView(NavigationView& nav) {
options_bloff.set_selected_index(0);
}
if (persistent_memory::clock_with_date()) {
options_clockformat.set_selected_index(1);
} else {
options_clockformat.set_selected_index(0);
}
checkbox_speaker.on_select = [this](Checkbox&, bool v) {
if (v) audio::output::speaker_mute(); //Just mute audio if speaker is disabled
@@ -276,7 +324,14 @@ SetUIView::SetUIView(NavigationView& nav) {
else
persistent_memory::set_config_backlight_timer(0);
if (checkbox_showclock.value()){
if (options_clockformat.selected_index() == 1)
persistent_memory::set_clock_with_date(true);
else
persistent_memory::set_clock_with_date(false);
}
persistent_memory::set_config_splash(checkbox_showsplash.value());
persistent_memory::set_clock_hidden(!checkbox_showclock.value());
//persistent_memory::set_config_login(checkbox_login.value());
nav.pop();
};
+51 -11
View File
@@ -51,8 +51,8 @@ public:
private:
Labels labels {
{ { 6 * 8, 7 * 16 }, "YYYY/MM/DD HH:MM:SS", Color::grey() },
{ { 10 * 8, 9 * 16 }, "/ / : :", Color::light_grey() }
{ { 6 * 8, 7 * 16 }, "YYYY-MM-DD HH:MM:SS", Color::grey() },
{ { 10 * 8, 9 * 16 }, "- - : :", Color::light_grey() }
};
NumberField field_year {
@@ -114,6 +114,7 @@ private:
struct SetFrequencyCorrectionModel {
int8_t ppm;
uint32_t freq;
};
class SetRadioView : public View {
@@ -130,6 +131,7 @@ private:
.background = Color::black(),
.foreground = Color::light_grey(),
};
uint8_t freq_step_khz = 3;
Text label_source {
{ 0, 1 * 16, 17 * 8, 16 },
@@ -147,8 +149,31 @@ private:
};
Labels labels_correction {
{ { 2 * 8, 4 * 16 }, "Frequency correction:", Color::light_grey() },
{ { 6 * 8, 5 * 16 }, "PPM", Color::light_grey() },
{ { 2 * 8, 3 * 16 }, "Frequency correction:", Color::light_grey() },
{ { 6 * 8, 4 * 16 }, "PPM", Color::light_grey() },
};
Checkbox check_clkout {
{ 18, (6 * 16 - 4) },
13,
"Enable CLKOUT"
};
NumberField field_clkout_freq {
{ 20 * 8, 6 * 16 },
5,
{ 10, 60000 },
1000,
' '
};
Labels labels_clkout_khz {
{ { 26 * 8, 6 * 16 }, "kHz", Color::light_grey() }
};
Text value_freq_step {
{ 21 * 8, (7 * 16 ), 4 * 8, 16 },
"| "
};
Labels labels_bias {
@@ -159,7 +184,7 @@ private:
};
NumberField field_ppm {
{ 2 * 8, 5 * 16 },
{ 2 * 8, 4 * 16 },
3,
{ -50, 50 },
1,
@@ -201,19 +226,19 @@ private:
};*/
Checkbox checkbox_speaker {
{ 3 * 8, 2 * 16 },
{ 3 * 8, 4 * 16 },
20,
"Hide H1 Speaker option"
};
Checkbox checkbox_bloff {
{ 3 * 8, 5 * 16 },
{ 3 * 8, 6 * 16 },
20,
"Backlight off after:"
};
OptionsField options_bloff {
{ 52, 6 * 16 + 8 },
10,
{ 52, 7 * 16 + 8 },
20,
{
{ "5 seconds", 5 },
{ "15 seconds", 15 },
@@ -227,10 +252,25 @@ private:
Checkbox checkbox_showsplash {
{ 3 * 8, 9 * 16 },
11,
20,
"Show splash"
};
Checkbox checkbox_showclock {
{ 3 * 8, 11 * 16 },
20,
"Show clock with:"
};
OptionsField options_clockformat {
{ 52, 12 * 16 + 8 },
20,
{
{ "time only", 0 },
{ "time and date", 1 }
}
};
Button button_ok {
{ 2 * 8, 16 * 16, 12 * 8, 32 },
"Save"
+10 -2
View File
@@ -43,7 +43,11 @@ SigGenView::~SigGenView() {
}
void SigGenView::update_config() {
baseband::set_siggen_config(transmitter_model.channel_bandwidth(), options_shape.selected_index_value(), field_stop.value());
if(checkbox_stop.value())
baseband::set_siggen_config(transmitter_model.channel_bandwidth(), options_shape.selected_index_value(), field_stop.value());
else
baseband::set_siggen_config(transmitter_model.channel_bandwidth(), options_shape.selected_index_value(), 0);
}
void SigGenView::update_tone() {
@@ -68,8 +72,10 @@ void SigGenView::start_tx() {
void SigGenView::on_tx_progress(const uint32_t progress, const bool done) {
(void) progress;
if (done)
if (done) {
transmitter_model.disable();
tx_view.set_transmitting(false);
}
}
SigGenView::SigGenView(
@@ -98,6 +104,8 @@ SigGenView::SigGenView(
options_shape.set_selected_index(0);
text_shape.set(shape_strings[0]);
field_stop.set_value(1);
symfield_tone.set_sym(1, 1); // Default: 1000 Hz
symfield_tone.on_change = [this]() {
if (auto_update)
+47 -3
View File
@@ -22,6 +22,7 @@
#include "ui_sonde.hpp"
#include "baseband_api.hpp"
#include "audio.hpp"
#include "portapack.hpp"
#include <cstring>
@@ -50,6 +51,8 @@ SondeView::SondeView(NavigationView& nav) {
&field_lna,
&field_vga,
&rssi,
&field_volume,
&check_beep,
&check_log,
&check_crc,
&text_signature,
@@ -63,6 +66,9 @@ SondeView::SondeView(NavigationView& nav) {
&button_see_map
});
// start from the frequency currently stored in the receiver_model:
target_frequency_ = receiver_model.tuning_frequency();
field_frequency.set_value(target_frequency_);
field_frequency.set_step(500); //euquiq: was 10000, but we are using this for fine-tunning
field_frequency.on_change = [this](rf::Frequency f) {
@@ -80,6 +86,10 @@ SondeView::SondeView(NavigationView& nav) {
geopos.set_read_only(true);
check_beep.on_select = [this](Checkbox&, bool v) {
beep = v;
};
check_log.on_select = [this](Checkbox&, bool v) {
logging = v;
};
@@ -107,16 +117,37 @@ SondeView::SondeView(NavigationView& nav) {
gps_info.lon,
999); //set a dummy heading out of range to draw a cross...probably not ideal?
};
logger = std::make_unique<SondeLogger>();
if (logger)
logger->append(u"sonde.txt");
// initialize audio:
field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99);
field_volume.on_change = [this](int32_t v) {
this->on_headphone_volume_changed(v);
};
audio::output::start();
audio::output::speaker_unmute();
// inject a PitchRSSIConfigureMessage in order to arm
// the pitch rssi events that will be used by the
// processor:
const PitchRSSIConfigureMessage message { true, 0 };
shared_memory.application_queue.push(message);
baseband::set_pitch_rssi(0, true);
}
SondeView::~SondeView() {
baseband::set_pitch_rssi(0, false);
radio::disable();
baseband::shutdown();
audio::output::stop();
}
void SondeView::focus() {
@@ -156,18 +187,31 @@ void SondeView::on_packet(const sonde::Packet &packet)
}
gps_info = packet.get_GPS_data();
geopos.set_altitude(gps_info.alt);
geopos.set_lat(gps_info.lat);
geopos.set_lon(gps_info.lon);
if (logger && logging)
if (logger && logging) {
logger->on_packet(packet);
}
if(beep) {
baseband::request_beep();
}
}
}
void SondeView::on_headphone_volume_changed(int32_t v) {
const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max;
receiver_model.set_headphone_volume(new_volume);
}
void SondeView::set_target_frequency(const uint32_t new_value) {
target_frequency_ = new_value;
radio::set_tuning_frequency(tuning_frequency());
//radio::set_tuning_frequency(tuning_frequency());
// we better remember the tuned frequency, by using this function instead:
receiver_model.set_tuning_frequency(target_frequency_);
}
uint32_t SondeView::tuning_frequency() const {
+28 -6
View File
@@ -55,7 +55,7 @@ class SondeView : public View {
public:
static constexpr uint32_t sampling_rate = 2457600;
static constexpr uint32_t baseband_bandwidth = 1750000;
SondeView(NavigationView& nav);
~SondeView();
@@ -68,10 +68,14 @@ private:
uint32_t target_frequency_ { 402700000 };
bool logging { false };
bool use_crc { false };
sonde::GPS_data gps_info;
sonde::temp_humid temp_humid_info;
std::string sonde_id;
bool beep { false };
sonde::GPS_data gps_info { };
sonde::temp_humid temp_humid_info { };
std::string sonde_id { };
// AudioOutput audio_output { };
Labels labels {
{ { 4 * 8, 2 * 16 }, "Type:", Color::light_grey() },
{ { 6 * 8, 3 * 16 }, "ID:", Color::light_grey() },
@@ -103,14 +107,29 @@ private:
{ 21 * 8, 0, 6 * 8, 4 },
};
NumberField field_volume {
{ 28 * 8, 0 * 16 },
2,
{ 0, 99 },
1,
' ',
};
Checkbox check_beep {
{ 22 * 8, 6 * 16 },
3,
"Beep"
};
Checkbox check_log {
{ 23 * 8, 6 * 16 },
{ 22 * 8, 8 * 16 },
3,
"Log"
};
Checkbox check_crc {
{ 23 * 8, 8 * 16 },
{ 22 * 8, 10 * 16 },
3,
"CRC"
};
@@ -170,7 +189,10 @@ private:
};
void on_packet(const sonde::Packet& packet);
void on_headphone_volume_changed(int32_t v);
void set_target_frequency(const uint32_t new_value);
uint32_t tuning_frequency() const;
};
+16 -7
View File
@@ -130,6 +130,13 @@ void set_afsk(const uint32_t baudrate, const uint32_t word_length, const uint32_
send_message(&message);
}
void set_aprs(const uint32_t baudrate) {
const APRSRxConfigureMessage message {
baudrate
};
send_message(&message);
}
void set_btle(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word) {
const BTLERxConfigureMessage message {
baudrate,
@@ -176,13 +183,18 @@ void kill_afsk() {
}
void set_audiotx_config(const uint32_t divider, const float deviation_hz, const float audio_gain,
const uint32_t tone_key_delta) {
const uint32_t tone_key_delta, const bool am_enabled, const bool dsb_enabled,
const bool usb_enabled, const bool lsb_enabled) {
const AudioTXConfigMessage message {
divider,
deviation_hz,
audio_gain,
tone_key_delta,
(float)persistent_memory::tone_mix() / 100.0f
(float)persistent_memory::tone_mix() / 100.0f,
am_enabled,
dsb_enabled,
usb_enabled,
lsb_enabled
};
send_message(&message);
}
@@ -224,11 +236,8 @@ void set_fsk_data(const uint32_t stream_length, const uint32_t samples_per_bit,
send_message(&message);
}
void set_pocsag(const pocsag::BitRate bitrate, bool phase) {
const POCSAGConfigureMessage message {
bitrate,
phase
};
void set_pocsag() {
const POCSAGConfigureMessage message {};
send_message(&message);
}
+4 -2
View File
@@ -61,13 +61,15 @@ void set_tones_config(const uint32_t bw, const uint32_t pre_silence, const uint1
void kill_tone();
void set_sstv_data(const uint8_t vis_code, const uint32_t pixel_duration);
void set_audiotx_config(const uint32_t divider, const float deviation_hz, const float audio_gain,
const uint32_t tone_key_delta);
const uint32_t tone_key_delta, const bool am_enabled, const bool dsb_enabled,
const bool usb_enabled, const bool lsb_enabled);
void set_fifo_data(const int8_t * data);
void set_pitch_rssi(int32_t avg, bool enabled);
void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phase_inc_mark, const uint32_t afsk_phase_inc_space,
const uint8_t afsk_repeat, const uint32_t afsk_bw, const uint8_t symbol_count);
void kill_afsk();
void set_afsk(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word);
void set_aprs(const uint32_t baudrate);
void set_btle(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word);
@@ -77,7 +79,7 @@ void set_ook_data(const uint32_t stream_length, const uint32_t samples_per_bit,
const uint32_t pause_symbols);
void set_fsk_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint32_t shift,
const uint32_t progress_notice);
void set_pocsag(const pocsag::BitRate bitrate, bool phase);
void set_pocsag();
void set_adsb();
void set_jammer(const bool run, const jammer::JammerType type, const uint32_t speed);
void set_rds_data(const uint16_t message_length);
File diff suppressed because it is too large Load Diff
+19
View File
@@ -21,6 +21,7 @@
#include "clock_manager.hpp"
#include "portapack_persistent_memory.hpp"
#include "portapack_io.hpp"
#include "hackrf_hal.hpp"
@@ -463,3 +464,21 @@ void ClockManager::stop_audio_pll() {
cgu::pll0audio::power_down();
while( cgu::pll0audio::is_locked() );
}
void ClockManager::enable_clock_output(bool enable) {
if(enable) {
clock_generator.enable_output(clock_generator_output_clkout);
if(portapack::persistent_memory::clkout_freq() < 1000) {
clock_generator.set_ms_frequency(clock_generator_output_clkout, portapack::persistent_memory::clkout_freq() * 128000, si5351_vco_f, 7);
} else {
clock_generator.set_ms_frequency(clock_generator_output_clkout, portapack::persistent_memory::clkout_freq() * 1000, si5351_vco_f, 0);
}
} else {
clock_generator.disable_output(clock_generator_output_clkout);
}
if(enable)
clock_generator.set_clock_control(clock_generator_output_clkout, si5351_clock_control_common[clock_generator_output_clkout].ms_src(get_reference_clock_generator_pll(reference.source)).clk_pdn(ClockControl::ClockPowerDown::Power_On));
else
clock_generator.set_clock_control(clock_generator_output_clkout, ClockControl::power_off());
}
+2
View File
@@ -79,6 +79,8 @@ public:
Reference get_reference() const;
void enable_clock_output(bool enable);
private:
I2C& i2c0;
si5351::Si5351& clock_generator;
+6
View File
@@ -298,6 +298,12 @@ void EventDispatcher::handle_switches() {
}
if( in_key_event ) {
if (switches_state[(size_t)ui::KeyEvent::Left] && switches_state[(size_t)ui::KeyEvent::Up])
{
const auto event = static_cast<ui::KeyEvent>(ui::KeyEvent::Back);
context.focus_manager().update(top_widget, event);
}
// If we're in a key event, return. We will ignore all additional key
// presses until the first key is released. We also want to ignore events
// where the last key held generates a key event when other pressed keys
+4
View File
@@ -140,6 +140,9 @@ Continuous (Fox-oring)
#include "sd_card.hpp"
#include <string.h>
#include "rffc507x.hpp" /* c/m, avoiding initial short ON Ant_DC_Bias pulse, from cold reset */
rffc507x::RFFC507x first_if;
static void event_loop() {
ui::Context context;
@@ -160,6 +163,7 @@ static void event_loop() {
}
int main(void) {
first_if.init(); /* To avoid initial short Ant_DC_Bias pulse ,we need quick set up GP01_RFF507X =1 */
if( portapack::init() ) {
portapack::display.init();
+11 -8
View File
@@ -83,7 +83,7 @@ TransmitterModel transmitter_model;
TemperatureLogger temperature_logger;
bool antenna_bias { false };
uint8_t bl_tick_counter { 0 };
uint32_t bl_tick_counter { 0 };
void set_antenna_bias(const bool v) {
antenna_bias = v;
@@ -179,20 +179,23 @@ static PortaPackModel portapack_model() {
static Optional<PortaPackModel> model;
if( !model.is_valid() ) {
if( audio_codec_wm8731.detected() ) {
model = PortaPackModel::R1_20150901;
} else {
/*For the time being, it is impossible to distinguish the hardware of R1 and R2 from the software level*/
/*At this point, I2c is not ready.*/
//if( audio_codec_wm8731.detected() ) {
// model = PortaPackModel::R1_20150901;
//} else {
model = PortaPackModel::R2_20170522;
}
//}
}
return model.value();
}
static audio::Codec* portapack_audio_codec() {
return (portapack_model() == PortaPackModel::R2_20170522)
? static_cast<audio::Codec*>(&audio_codec_ak4951)
: static_cast<audio::Codec*>(&audio_codec_wm8731)
/* I2C ready OK, Automatic recognition of audio chip */
return (audio_codec_wm8731.detected())
? static_cast<audio::Codec*>(&audio_codec_wm8731)
: static_cast<audio::Codec*>(&audio_codec_ak4951)
;
}
+1 -1
View File
@@ -54,7 +54,7 @@ extern TransmitterModel transmitter_model;
extern bool speaker_mode;
void set_speaker_mode(const bool v);
extern uint8_t bl_tick_counter;
extern uint32_t bl_tick_counter;
extern bool antenna_bias;
extern TemperatureLogger temperature_logger;
+6 -2
View File
@@ -38,9 +38,13 @@ void make_aprs_frame(const char * src_address, const uint32_t src_ssid,
char address[14] = { 0 };
memcpy(&address[0], dest_address, 6);
address[6] = (dest_ssid & 15) << 1;
memcpy(&address[7], src_address, 6);
address[13] = (src_ssid & 15) << 1;
//euquiq: According to ax.25 doc section 2.2.13.x.x and 2.4.1.2
// SSID need bits 5.6 set, so later when shifted it will end up being 011xxxx0 (xxxx = SSID number)
// Notice that if need to signal usage of AX.25 V2.0, (dest_ssid | 112); (MSb will need to be set at the end)
address[6] = (dest_ssid | 48);
address[13] = (src_ssid | 48);
frame.make_ui_frame(address, 0x03, protocol_id_t::NO_LAYER3, payload);
}
+20 -15
View File
@@ -51,32 +51,37 @@ void AX25Frame::NRZI_add_bit(const uint32_t bit) {
}
}
void AX25Frame::add_byte(uint8_t byte, bool is_flag, bool is_data) {
uint32_t bit;
void AX25Frame::add_byte(uint8_t byte, bool is_flag, bool is_data)
{
bool bit;
if (is_data)
crc_ccitt.process_byte(byte);
for (uint32_t i = 0; i < 8; i++) {
for (uint32_t i = 0; i < 8; i++)
{
bit = (byte >> i) & 1;
NRZI_add_bit(bit);
if (bit)
{
ones_counter++;
if ((ones_counter == 5) && (!is_flag))
{
NRZI_add_bit(0);
ones_counter = 0;
}
}
else
ones_counter = 0;
if ((ones_counter == 6) && (!is_flag)) {
NRZI_add_bit(0);
ones_counter = 0;
}
NRZI_add_bit(bit);
}
}
void AX25Frame::flush() {
void AX25Frame::flush()
{
if (bit_counter)
*bb_data_ptr = current_byte << (7 - bit_counter);
*bb_data_ptr = current_byte << (8 - bit_counter); //euquiq: This was 7 but there are 8 bits
};
void AX25Frame::add_flag() {
+6 -8
View File
@@ -111,14 +111,12 @@ void set_direction(const rf::Direction new_direction) {
// Hack to fix the CPLD (clocking ?) bug: toggle CPLD SRAM overlay depending on new direction
// Use CPLD's EEPROM config when transmitting
// Use the SRAM overlay when receiving
if (direction != new_direction) {
if (new_direction == rf::Direction::Transmit) {
hackrf::cpld::init_from_eeprom();
} else {
if( !hackrf::cpld::load_sram() ) {
chSysHalt();
}
}
// teixeluis: undone "Hack to fix the CPLD (clocking ?) bug".
// Apparently with current CPLD code from the hackrf repo,
// toggling CPLD overlay should no longer be necessary:
if (direction != new_direction && new_direction == rf::Direction::Transmit) {
hackrf::cpld::init_from_eeprom();
}
direction = new_direction;
+8
View File
@@ -171,7 +171,11 @@ void ReceiverModel::enable() {
update_baseband_bandwidth();
update_sampling_rate();
update_modulation();
// TODO: would challenge if this should belong to the
// receiver_model namespace:
update_headphone_volume();
led_rx.on();
}
@@ -182,6 +186,10 @@ void ReceiverModel::disable() {
// TODO: Responsibility for enabling/disabling the radio is muddy.
// Some happens in ReceiverModel, some inside radio namespace.
radio::disable();
// TODO: we are doing this repeatedly in different levels of the
// call stack. Keeping it for now, but there seem to be too many
// redundant calls:
led_rx.off();
}
+19 -1
View File
@@ -179,6 +179,24 @@ public:
}
}
}
else if( event == ui::KeyEvent::Up ) {
if(selected_key == recent.front().key()){
return false;
}
else {
advance(-1);
return true;
}
}
else if( event == ui::KeyEvent::Down ) {
if( selected_key == recent.back().key()) {
return false;
}
else {
advance(1);
return true;
}
}
return false;
}
@@ -264,7 +282,7 @@ public:
// TODO: What happens here shouldn't matter if I do proper damage detection!
}
void on_focus() override {
void focus() override {
_table.focus();
}
+24 -7
View File
@@ -112,6 +112,23 @@ std::string to_string_dec_int(
return q;
}
std::string to_string_decimal(float decimal, int8_t precision) {
double integer_part;
double fractional_part;
std::string result;
fractional_part = modf(decimal, &integer_part) * pow(10, precision);
if (fractional_part < 0) {
fractional_part = -fractional_part;
}
result = to_string_dec_int(integer_part) + "." + to_string_dec_uint(fractional_part, precision, '0');
return result;
}
std::string to_string_short_freq(const uint64_t f) {
auto final_str = to_string_dec_int(f / 1000000,4) + "." + to_string_dec_int((f / 100) % 10000, 4, '0');
return final_str;
@@ -165,8 +182,8 @@ std::string to_string_datetime(const rtc::RTC& value, const TimeFormat format) {
std::string string { "" };
if (format == YMDHMS) {
string += to_string_dec_uint(value.year(), 4) + "/" +
to_string_dec_uint(value.month(), 2, '0') + "/" +
string += to_string_dec_uint(value.year(), 4) + "-" +
to_string_dec_uint(value.month(), 2, '0') + "-" +
to_string_dec_uint(value.day(), 2, '0') + " ";
}
@@ -189,11 +206,11 @@ std::string to_string_timestamp(const rtc::RTC& value) {
}
std::string to_string_FAT_timestamp(const FATTimestamp& timestamp) {
return to_string_dec_uint((timestamp.FAT_date >> 9) + 1980) + "/" +
to_string_dec_uint((timestamp.FAT_date >> 5) & 0xF, 2) + "/" +
to_string_dec_uint((timestamp.FAT_date & 0x1F), 2) + " " +
to_string_dec_uint((timestamp.FAT_time >> 11), 2) + ":" +
to_string_dec_uint((timestamp.FAT_time >> 5) & 0x3F, 2);
return to_string_dec_uint((timestamp.FAT_date >> 9) + 1980) + "-" +
to_string_dec_uint((timestamp.FAT_date >> 5) & 0xF, 2, '0') + "-" +
to_string_dec_uint((timestamp.FAT_date & 0x1F), 2, '0') + " " +
to_string_dec_uint((timestamp.FAT_time >> 11), 2, '0') + ":" +
to_string_dec_uint((timestamp.FAT_time >> 5) & 0x3F, 2, '0');
}
std::string unit_auto_scale(double n, const uint32_t base_nano, uint32_t precision) {
+2
View File
@@ -43,6 +43,8 @@ const char unit_prefix[7] { 'n', 'u', 'm', 0, 'k', 'M', 'G' };
std::string to_string_bin(const uint32_t n, const uint8_t l = 0);
std::string to_string_dec_uint(const uint32_t n, const int32_t l = 0, const char fill = ' ');
std::string to_string_dec_int(const int32_t n, const int32_t l = 0, const char fill = 0);
std::string to_string_decimal(float decimal, int8_t precision);
std::string to_string_hex(const uint64_t n, const int32_t l = 0);
std::string to_string_hex_array(uint8_t * const array, const int32_t l = 0);
+8 -11
View File
@@ -63,17 +63,9 @@ GeoPos::GeoPos(
const auto changed_fn = [this](int32_t) {
float lat_value = lat();
float lon_value = lon();
double integer_part;
double fractional_part;
fractional_part = modf(lat_value, &integer_part) * 100000;
if (fractional_part < 0)
fractional_part = -fractional_part;
text_lat_decimal.set(to_string_dec_int(integer_part) + "." + to_string_dec_uint(fractional_part, 5));
fractional_part = modf(lon_value, &integer_part) * 100000;
if (fractional_part < 0)
fractional_part = -fractional_part;
text_lon_decimal.set(to_string_dec_int(integer_part) + "." + to_string_dec_uint(fractional_part, 5));
text_lat_decimal.set(to_string_decimal(lat_value, 5));
text_lon_decimal.set(to_string_decimal(lon_value, 5));
if (on_change && report_change)
on_change(altitude(), lat_value, lon_value);
@@ -264,8 +256,13 @@ void GeoMapView::focus() {
void GeoMapView::update_position(float lat, float lon, uint16_t angle) {
lat_ = lat;
lon_ = lon;
// Stupid hack to avoid an event loop
geopos.set_report_change(false);
geopos.set_lat(lat_);
geopos.set_lon(lon_);
geopos.set_report_change(true);
geomap.set_angle(angle);
geomap.move(lon_, lat_);
geomap.set_dirty();
+2 -1
View File
@@ -75,8 +75,9 @@ void RSSI::paint(Painter& painter) {
Color::black()
);
if (pitch_rssi_enabled)
if (pitch_rssi_enabled) {
baseband::set_pitch_rssi((avg_ - raw_min) * 2000 / raw_delta, true);
}
}
void RSSI::set_pitch_rssi(bool enabled) {
+23 -32
View File
@@ -94,13 +94,16 @@ void FrequencyScale::set_spectrum_sampling_rate(const int new_sampling_rate) {
}
void FrequencyScale::set_channel_filter(
const int pass_frequency,
const int stop_frequency
const int low_frequency,
const int high_frequency,
const int transition
) {
if( (channel_filter_pass_frequency != pass_frequency) ||
(channel_filter_stop_frequency != stop_frequency) ) {
channel_filter_pass_frequency = pass_frequency;
channel_filter_stop_frequency = stop_frequency;
if( (channel_filter_low_frequency != low_frequency) ||
(channel_filter_high_frequency != high_frequency) ||
(channel_filter_transition != transition) ) {
channel_filter_low_frequency = low_frequency;
channel_filter_high_frequency = high_frequency;
channel_filter_transition = transition;
set_dirty();
}
}
@@ -184,41 +187,28 @@ void FrequencyScale::draw_frequency_ticks(Painter& painter, const Rect r) {
}
void FrequencyScale::draw_filter_ranges(Painter& painter, const Rect r) {
if( channel_filter_pass_frequency ) {
if( channel_filter_low_frequency != channel_filter_high_frequency ) {
const auto x_center = r.width() / 2;
const auto pass_offset = channel_filter_pass_frequency * spectrum_bins / spectrum_sampling_rate;
const auto stop_offset = channel_filter_stop_frequency * spectrum_bins / spectrum_sampling_rate;
const auto x_low = x_center + channel_filter_low_frequency * spectrum_bins / spectrum_sampling_rate;
const auto x_high = x_center + channel_filter_high_frequency * spectrum_bins / spectrum_sampling_rate;
const auto pass_x_lo = x_center - pass_offset;
const auto pass_x_hi = x_center + pass_offset;
if( channel_filter_transition ) {
const auto trans = channel_filter_transition * spectrum_bins / spectrum_sampling_rate;
if( channel_filter_stop_frequency ) {
const auto stop_x_lo = x_center - stop_offset;
const auto stop_x_hi = x_center + stop_offset;
const Rect r_stop_lo {
r.left() + stop_x_lo, r.bottom() - filter_band_height,
pass_x_lo - stop_x_lo, filter_band_height
const Rect r_all {
r.left() + x_low - trans, r.bottom() - filter_band_height,
x_high - x_low + trans*2, filter_band_height
};
painter.fill_rectangle(
r_stop_lo,
Color::yellow()
);
const Rect r_stop_hi {
r.left() + pass_x_hi, r.bottom() - filter_band_height,
stop_x_hi - pass_x_hi, filter_band_height
};
painter.fill_rectangle(
r_stop_hi,
r_all,
Color::yellow()
);
}
const Rect r_pass {
r.left() + pass_x_lo, r.bottom() - filter_band_height,
pass_x_hi - pass_x_lo, filter_band_height
r.left() + x_low, r.bottom() - filter_band_height,
x_high - x_low, filter_band_height
};
painter.fill_rectangle(
r_pass,
@@ -390,8 +380,9 @@ void WaterfallWidget::on_channel_spectrum(const ChannelSpectrum& spectrum) {
sampling_rate = spectrum.sampling_rate;
frequency_scale.set_spectrum_sampling_rate(sampling_rate);
frequency_scale.set_channel_filter(
spectrum.channel_filter_pass_frequency,
spectrum.channel_filter_stop_frequency
spectrum.channel_filter_low_frequency,
spectrum.channel_filter_high_frequency,
spectrum.channel_filter_transition
);
}
+4 -3
View File
@@ -82,7 +82,7 @@ public:
bool on_key(const KeyEvent key) override;
void set_spectrum_sampling_rate(const int new_sampling_rate);
void set_channel_filter(const int pass_frequency, const int stop_frequency);
void set_channel_filter(const int low_frequency, const int high_frequency, const int transition);
void paint(Painter& painter) override;
@@ -96,8 +96,9 @@ private:
SignalToken signal_token_tick_second { };
int spectrum_sampling_rate { 0 };
const int spectrum_bins = std::tuple_size<decltype(ChannelSpectrum::db)>::value;
int channel_filter_pass_frequency { 0 };
int channel_filter_stop_frequency { 0 };
int channel_filter_low_frequency { 0 };
int channel_filter_high_frequency { 0 };
int channel_filter_transition { 0 };
void clear();
void clear_background(Painter& painter, const Rect r);
@@ -103,6 +103,7 @@ void TransmitterView::set_transmitting(const bool transmitting) {
void TransmitterView::on_show() {
field_frequency.set_value(transmitter_model.tuning_frequency());
field_frequency_step.set_by_value(receiver_model.frequency_step());
field_gain.set_value(transmitter_model.tx_gain());
field_amp.set_value(transmitter_model.rf_amp() ? 14 : 0);
@@ -122,6 +123,7 @@ TransmitterView::TransmitterView(
add_children({
&field_frequency,
&field_frequency_step,
&text_gain,
&field_gain,
&button_start,
@@ -157,6 +159,10 @@ TransmitterView::TransmitterView(
if (on_edit_frequency)
on_edit_frequency();
};
field_frequency_step.on_change = [this](size_t, OptionsField::value_t v) {
this->field_frequency.set_step(v);
};
field_gain.on_change = [this](uint32_t tx_gain) {
on_tx_gain_changed(tx_gain);
+7 -3
View File
@@ -124,11 +124,11 @@ private:
};
Text text_bw {
{ 11 * 8, 1 * 8, 9 * 8, 1 * 16 },
"BW: kHz"
{ 18 * 8, 1 * 8, 3 * 8, 1 * 16 },
"kHz"
};
NumberField field_bw {
{ 14 * 8, 1 * 8 },
{ 15 * 8, 1 * 8 },
3,
{ 1, 150 },
1,
@@ -152,6 +152,10 @@ private:
{ 21 * 8, 1 * 8, 9 * 8, 32 },
"START"
};
FrequencyStepView field_frequency_step {
{ 10 * 8 - 4, 1 * 8 },
};
void on_tuning_frequency_changed(rf::Frequency f);
void on_channel_bandwidth_changed(uint32_t channel_bandwidth);
+1 -1
View File
@@ -182,7 +182,7 @@ void TVView::clear() {
/* TVWidget *******************************************************/
TVWidget::TVWidget(const bool cursor) {
TVWidget::TVWidget() {
add_children({
&tv_view,
&field_xcorr
+1 -1
View File
@@ -92,7 +92,7 @@ class TVWidget : public View {
public:
std::function<void(int32_t offset)> on_select { };
TVWidget(const bool cursor = false);
TVWidget();
TVWidget(const TVWidget&) = delete;
TVWidget(TVWidget&&) = delete;
+51 -14
View File
@@ -34,6 +34,7 @@
#include "ui_adsb_rx.hpp"
#include "ui_adsb_tx.hpp"
#include "ui_afsk_rx.hpp"
#include "ui_aprs_rx.hpp"
#include "ui_btle_rx.hpp"
#include "ui_nrf_rx.hpp"
#include "ui_aprs_tx.hpp"
@@ -67,7 +68,7 @@
#include "ui_view_wav.hpp"
#include "ui_whipcalc.hpp"
#include "acars_app.hpp"
//#include "acars_app.hpp"
#include "ais_app.hpp"
#include "analog_audio_app.hpp"
#include "analog_tv_app.hpp"
@@ -81,7 +82,7 @@
#include "tpms_app.hpp"
#include "core_control.hpp"
#include "ui_looking_glass_app.hpp"
#include "file.hpp"
#include "png_writer.hpp"
@@ -113,7 +114,7 @@ SystemStatusView::SystemStatusView(
&button_camera,
&button_sleep,
&button_bias_tee,
&image_clock_status,
&button_clock_status,
&sd_card_status_view,
});
@@ -168,6 +169,10 @@ SystemStatusView::SystemStatusView(
DisplaySleepMessage message;
EventDispatcher::send_message(message);
};
button_clock_status.on_select = [this](ImageButton&) {
this->on_clk();
};
}
void SystemStatusView::refresh() {
@@ -188,11 +193,16 @@ void SystemStatusView::refresh() {
}
if (portapack::clock_manager.get_reference().source == ClockManager::ReferenceSource::External) {
image_clock_status.set_bitmap(&bitmap_icon_clk_ext);
button_bias_tee.set_foreground(ui::Color::green());
button_clock_status.set_bitmap(&bitmap_icon_clk_ext);
// button_bias_tee.set_foreground(ui::Color::green()); Typo?
} else {
image_clock_status.set_bitmap(&bitmap_icon_clk_int);
button_bias_tee.set_foreground(ui::Color::light_grey());
button_clock_status.set_bitmap(&bitmap_icon_clk_int);
// button_bias_tee.set_foreground(ui::Color::green());
}
if(portapack::persistent_memory::clkout_enabled()) {
button_clock_status.set_foreground(ui::Color::green());
} else {
button_clock_status.set_foreground(ui::Color::light_grey());
}
set_dirty();
@@ -302,6 +312,18 @@ void SystemStatusView::on_camera() {
}
}
void SystemStatusView::on_clk() {
bool v = portapack::persistent_memory::clkout_enabled();
if(v) {
v = false;
} else {
v = true;
}
portapack::clock_manager.enable_clock_output(v);
portapack::persistent_memory::set_clkout_enabled(v);
refresh();
}
void SystemStatusView::on_title() {
if(nav_.is_top())
nav_.push<AboutView>();
@@ -328,13 +350,21 @@ InformationView::InformationView(
});
version.set_style(&style_infobar);
ltime.set_hide_clock(portapack::persistent_memory::hide_clock());
ltime.set_style(&style_infobar);
ltime.set_seconds_enabled(true);
ltime.set_date_enabled(false);
ltime.set_date_enabled(portapack::persistent_memory::clock_with_date());
set_dirty();
}
void InformationView::refresh() {
ltime.set_hide_clock(portapack::persistent_memory::hide_clock());
ltime.set_seconds_enabled(true);
ltime.set_date_enabled(portapack::persistent_memory::clock_with_date());
}
/* Navigation ************************************************************/
bool NavigationView::is_top() const {
@@ -365,6 +395,7 @@ void NavigationView::pop() {
update_view();
}
}
void NavigationView::pop_modal() {
@@ -447,7 +478,8 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) {
{ "POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav](){ nav.push<POCSAGAppView>(); } },
{ "Radiosnde", ui::Color::green(), &bitmap_icon_sonde, [&nav](){ nav.push<SondeView>(); } },
{ "TPMS Cars", ui::Color::green(), &bitmap_icon_tpms, [&nav](){ nav.push<TPMSAppView>(); } },
/*{ "APRS", ui::Color::dark_grey(), &bitmap_icon_aprs, [&nav](){ nav.push<NotImplementedView>(); } },
{ "APRS", ui::Color::green(), &bitmap_icon_aprs, [&nav](){ nav.push<APRSRXView>(); } }
/*
{ "DMR", ui::Color::dark_grey(), &bitmap_icon_dmr, [&nav](){ nav.push<NotImplementedView>(); } },
{ "SIGFOX", ui::Color::dark_grey(), &bitmap_icon_fox, [&nav](){ nav.push<NotImplementedView>(); } }, // SIGFRXView
{ "LoRa", ui::Color::dark_grey(), &bitmap_icon_lora, [&nav](){ nav.push<NotImplementedView>(); } },
@@ -464,10 +496,10 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
add_items({
//{ "..", ui::Color::light_grey(),&bitmap_icon_previous, [&nav](){ nav.pop(); } },
{ "ADS-B [S]", ui::Color::yellow(), &bitmap_icon_adsb, [&nav](){ nav.push<ADSBTxView>(); } },
{ "APRS", ui::Color::orange(), &bitmap_icon_aprs, [&nav](){ nav.push<APRSTXView>(); } },
{ "APRS", ui::Color::green(), &bitmap_icon_aprs, [&nav](){ nav.push<APRSTXView>(); } },
{ "BHT Xy/EP", ui::Color::green(), &bitmap_icon_bht, [&nav](){ nav.push<BHTView>(); } },
{ "GPS Sim", ui::Color::yellow(), &bitmap_icon_gps_sim, [&nav](){ nav.push<GpsSimAppView>(); } },
{ "Jammer", ui::Color::yellow(), &bitmap_icon_jammer, [&nav](){ nav.push<JammerView>(); } },
{ "Jammer", ui::Color::green(), &bitmap_icon_jammer, [&nav](){ nav.push<JammerView>(); } },
{ "Key fob", ui::Color::orange(), &bitmap_icon_keyfob, [&nav](){ nav.push<KeyfobView>(); } },
{ "LGE tool", ui::Color::yellow(), &bitmap_icon_lge, [&nav](){ nav.push<LGEView>(); } },
{ "Morse", ui::Color::green(), &bitmap_icon_morse, [&nav](){ nav.push<MorseView>(); } },
@@ -522,8 +554,9 @@ SystemMenuView::SystemMenuView(NavigationView& nav) {
{ "Capture", ui::Color::red(), &bitmap_icon_capture, [&nav](){ nav.push<CaptureAppView>(); } },
{ "Replay", ui::Color::green(), &bitmap_icon_replay, [&nav](){ nav.push<ReplayAppView>(); } },
{ "Calls", ui::Color::yellow(), &bitmap_icon_search, [&nav](){ nav.push<SearchView>(); } },
{ "Scanner", ui::Color::green(), &bitmap_icon_scanner, [&nav](){ nav.push<ScannerView>(); } },
{ "Scanner", ui::Color::yellow(), &bitmap_icon_scanner, [&nav](){ nav.push<ScannerView>(); } },
{ "Microphone", ui::Color::yellow(), &bitmap_icon_microphone,[&nav](){ nav.push<MicTXView>(); } },
{ "Looking Glass", ui::Color::yellow(), &bitmap_icon_looking, [&nav](){ nav.push<GlassView>(); } },
{ "Tools", ui::Color::cyan(), &bitmap_icon_utilities, [&nav](){ nav.push<UtilitiesMenuView>(); } },
{ "Options", ui::Color::cyan(), &bitmap_icon_setup, [&nav](){ nav.push<SettingsMenuView>(); } },
{ "Debug", ui::Color::light_grey(), &bitmap_icon_debug, [&nav](){ nav.push<DebugMenuView>(); } },
@@ -582,6 +615,7 @@ SystemView::SystemView(
}
else{
add_child(&info_view);
info_view.refresh();
}
this->status_view.set_back_enabled(!this->navigation_view.is_top());
@@ -603,7 +637,9 @@ SystemView::SystemView(
navigation_view.push<SystemMenuView>();
if (portapack::persistent_memory::config_splash())
{
navigation_view.push<BMPView>();
}
status_view.set_back_enabled(false);
status_view.set_title_image_enabled(true);
status_view.set_dirty();
@@ -634,7 +670,8 @@ BMPView::BMPView(NavigationView& nav) {
}
void BMPView::paint(Painter&) {
portapack::display.drawBMP({(240 - 230) / 2, (320 - 50) / 2 - 10}, splash_bmp, false);
if(!portapack::display.drawBMP2({ 0, 0 }, "splash.bmp"))
portapack::display.drawBMP({(240 - 230) / 2, (320 - 50) / 2 - 10}, splash_bmp, false);
}
/* NotImplementedView ****************************************************/
+249 -249
View File
@@ -45,252 +45,252 @@
using namespace sd_card;
namespace ui {
namespace ui
{
enum modal_t {
INFO = 0,
YESNO,
YESCANCEL,
ABORT
};
class NavigationView : public View {
public:
std::function<void(const View&)> on_view_changed { };
NavigationView() = default;
NavigationView(const NavigationView&) = delete;
NavigationView(NavigationView&&) = delete;
NavigationView& operator=(const NavigationView&) = delete;
NavigationView& operator=(NavigationView&&) = delete;
bool is_top() const;
template<class T, class... Args>
T* push(Args&&... args) {
return reinterpret_cast<T*>(push_view(std::unique_ptr<View>(new T(*this, std::forward<Args>(args)...))));
}
template<class T, class... Args>
T* replace(Args&&... args) {
pop();
return reinterpret_cast<T*>(push_view(std::unique_ptr<View>(new T(*this, std::forward<Args>(args)...))));
}
void push(View* v);
void replace(View* v);
void pop();
void pop_modal();
void display_modal(const std::string& title, const std::string& message);
void display_modal(const std::string& title, const std::string& message, const modal_t type, const std::function<void(bool)> on_choice = nullptr);
void focus() override;
private:
std::vector<std::unique_ptr<View>> view_stack { };
Widget* modal_view { nullptr };
Widget* view() const;
void free_view();
void update_view();
View* push_view(std::unique_ptr<View> new_view);
};
class SystemStatusView : public View {
public:
std::function<void(void)> on_back { };
SystemStatusView(NavigationView& nav);
void set_back_enabled(bool new_value);
void set_title_image_enabled(bool new_value);
void set_title(const std::string new_value);
private:
static constexpr auto default_title = "";
NavigationView& nav_;
Rectangle backdrop {
{ 0 * 8, 0 * 16, 240, 16 },
Color::dark_grey()
enum modal_t
{
INFO = 0,
YESNO,
YESCANCEL,
ABORT
};
ImageButton button_back {
{ 2, 0 * 16, 16, 16 },
&bitmap_icon_previous,
Color::white(),
Color::dark_grey()
class NavigationView : public View
{
public:
std::function<void(const View &)> on_view_changed{};
NavigationView() = default;
NavigationView(const NavigationView &) = delete;
NavigationView(NavigationView &&) = delete;
NavigationView &operator=(const NavigationView &) = delete;
NavigationView &operator=(NavigationView &&) = delete;
bool is_top() const;
template <class T, class... Args>
T *push(Args &&...args)
{
return reinterpret_cast<T *>(push_view(std::unique_ptr<View>(new T(*this, std::forward<Args>(args)...))));
}
template <class T, class... Args>
T *replace(Args &&...args)
{
pop();
return reinterpret_cast<T *>(push_view(std::unique_ptr<View>(new T(*this, std::forward<Args>(args)...))));
}
void push(View *v);
void replace(View *v);
void pop();
void pop_modal();
void display_modal(const std::string &title, const std::string &message);
void display_modal(const std::string &title, const std::string &message, const modal_t type, const std::function<void(bool)> on_choice = nullptr);
void focus() override;
private:
std::vector<std::unique_ptr<View>> view_stack{};
Widget *modal_view{nullptr};
Widget *view() const;
void free_view();
void update_view();
View *push_view(std::unique_ptr<View> new_view);
};
Text title {
{ 20, 0, 14 * 8, 1 * 16 },
default_title,
};
class SystemStatusView : public View
{
public:
std::function<void(void)> on_back{};
ImageButton button_title {
{2, 0, 80, 16},
&bitmap_titlebar_image,
Color::white(),
Color::dark_grey()
};
SystemStatusView(NavigationView &nav);
ImageButton button_speaker {
{ 17 * 8, 0, 2 * 8, 1 * 16 },
&bitmap_icon_speaker_mute,
Color::light_grey(),
Color::dark_grey()
};
ImageButton button_stealth {
{ 19 * 8, 0, 2 * 8, 1 * 16 },
&bitmap_icon_stealth,
Color::light_grey(),
Color::dark_grey()
};
/*ImageButton button_textentry {
void set_back_enabled(bool new_value);
void set_title_image_enabled(bool new_value);
void set_title(const std::string new_value);
private:
static constexpr auto default_title = "";
NavigationView &nav_;
Rectangle backdrop{
{0 * 8, 0 * 16, 240, 16},
Color::dark_grey()};
ImageButton button_back{
{2, 0 * 16, 16, 16},
&bitmap_icon_previous,
Color::white(),
Color::dark_grey()};
Text title{
{20, 0, 14 * 8, 1 * 16},
default_title,
};
ImageButton button_title{
{2, 0, 80, 16},
&bitmap_titlebar_image,
Color::white(),
Color::dark_grey()};
ImageButton button_speaker{
{17 * 8, 0, 2 * 8, 1 * 16},
&bitmap_icon_speaker_mute,
Color::light_grey(),
Color::dark_grey()};
ImageButton button_stealth{
{19 * 8, 0, 2 * 8, 1 * 16},
&bitmap_icon_stealth,
Color::light_grey(),
Color::dark_grey()};
/*ImageButton button_textentry {
{ 170, 0, 2 * 8, 1 * 16 },
&bitmap_icon_unistroke,
Color::white(),
Color::dark_grey()
};*/
ImageButton button_camera {
{ 21 * 8, 0, 2 * 8, 1 * 16 },
&bitmap_icon_camera,
Color::white(),
Color::dark_grey()
ImageButton button_camera{
{21 * 8, 0, 2 * 8, 1 * 16},
&bitmap_icon_camera,
Color::white(),
Color::dark_grey()};
ImageButton button_sleep{
{23 * 8, 0, 2 * 8, 1 * 16},
&bitmap_icon_sleep,
Color::white(),
Color::dark_grey()};
ImageButton button_bias_tee{
{25 * 8, 0, 12, 1 * 16},
&bitmap_icon_biast_off,
Color::light_grey(),
Color::dark_grey()};
ImageButton button_clock_status{
{27 * 8, 0 * 16, 2 * 8, 1 * 16},
&bitmap_icon_clk_int,
Color::light_grey(),
Color::dark_grey()};
SDCardStatusView sd_card_status_view{
{28 * 8, 0 * 16, 2 * 8, 1 * 16}};
void on_speaker();
void on_stealth();
void on_bias_tee();
//void on_textentry();
void on_camera();
void on_title();
void refresh();
void on_clk();
MessageHandlerRegistration message_handler_refresh{
Message::ID::StatusRefresh,
[this](const Message *const p)
{
(void)p;
this->refresh();
}};
};
ImageButton button_sleep {
{ 23 * 8, 0, 2 * 8, 1 * 16 },
&bitmap_icon_sleep,
Color::white(),
Color::dark_grey()
};
ImageButton button_bias_tee {
{ 25 * 8, 0, 12, 1 * 16 },
&bitmap_icon_biast_off,
Color::light_grey(),
Color::dark_grey()
};
Image image_clock_status {
{ 27 * 8, 0 * 16, 2 * 8, 1 * 16 },
&bitmap_icon_clk_int,
Color::light_grey(),
Color::dark_grey()
};
SDCardStatusView sd_card_status_view {
{ 28 * 8, 0 * 16, 2 * 8, 1 * 16 }
class InformationView : public View
{
public:
InformationView(NavigationView &nav);
void refresh();
private:
static constexpr auto version_string = "v1.4.2";
NavigationView &nav_;
Rectangle backdrop{
{0, 0 * 16, 240, 16},
{33, 33, 33}};
Text version{
{2, 0, 11 * 8, 16},
version_string};
LiveDateTime ltime{
{86, 0, 19 * 8, 16}};
};
void on_speaker();
void on_stealth();
void on_bias_tee();
//void on_textentry();
void on_camera();
void on_title();
void refresh();
MessageHandlerRegistration message_handler_refresh {
Message::ID::StatusRefresh,
[this](const Message* const p) {
(void)p;
this->refresh();
}
};
};
class BMPView : public View
{
public:
BMPView(NavigationView &nav);
void paint(Painter &) override;
void focus() override;
class InformationView : public View {
public:
InformationView(NavigationView& nav);
private:
static constexpr auto version_string = "v1.2.1";
NavigationView& nav_;
private:
Text text_info{
{4 * 8, 284, 20 * 8, 16},
"Version " VERSION_STRING};
Rectangle backdrop {
{ 0, 0 * 16, 240, 16 },
{33, 33, 33}
Button button_done{
{240, 0, 1, 1},
""};
};
Text version {
{2, 0, 11 * 8, 16},
version_string
class ReceiversMenuView : public BtnGridView
{
public:
ReceiversMenuView(NavigationView &nav);
std::string title() const override { return "Receivers"; };
};
LiveDateTime ltime {
{174, 0, 8 * 8, 16}
class TransmittersMenuView : public BtnGridView
{
public:
TransmittersMenuView(NavigationView &nav);
std::string title() const override { return "Transmitters"; };
};
};
class BMPView : public View {
public:
BMPView(NavigationView& nav);
void paint(Painter&) override;
void focus() override;
private:
Text text_info {
{ 4*8, 284, 20 * 8, 16 },
"Version " VERSION_STRING
class UtilitiesMenuView : public BtnGridView
{
public:
UtilitiesMenuView(NavigationView &nav);
std::string title() const override { return "Utilities"; };
};
Button button_done {
{ 240, 0, 1, 1 },
""
class SystemMenuView : public BtnGridView
{
public:
SystemMenuView(NavigationView &nav);
private:
void hackrf_mode(NavigationView &nav);
};
};
class ReceiversMenuView : public BtnGridView {
public:
ReceiversMenuView(NavigationView& nav);
std::string title() const override { return "Receivers"; };
};
class SystemView : public View
{
public:
SystemView(
Context &context,
const Rect parent_rect);
class TransmittersMenuView : public BtnGridView {
public:
TransmittersMenuView(NavigationView& nav);
std::string title() const override { return "Transmitters"; };
};
Context &context() const override;
class UtilitiesMenuView : public BtnGridView {
public:
UtilitiesMenuView(NavigationView& nav);
std::string title() const override { return "Utilities"; };
};
private:
SystemStatusView status_view{navigation_view};
InformationView info_view{navigation_view};
NavigationView navigation_view{};
Context &context_;
};
class SystemMenuView : public BtnGridView {
public:
SystemMenuView(NavigationView& nav);
private:
void hackrf_mode(NavigationView& nav);
};
class SystemView : public View {
public:
SystemView(
Context& context,
const Rect parent_rect
);
Context& context() const override;
private:
SystemStatusView status_view { navigation_view };
InformationView info_view { navigation_view };
NavigationView navigation_view { };
Context& context_;
};
/*class NotImplementedView : public View {
/*class NotImplementedView : public View {
public:
NotImplementedView(NavigationView& nav);
@@ -308,43 +308,43 @@ private:
};
};*/
class ModalMessageView : public View {
public:
ModalMessageView(
NavigationView& nav,
const std::string& title,
const std::string& message,
const modal_t type,
const std::function<void(bool)> on_choice
);
void paint(Painter& painter) override;
void focus() override;
class ModalMessageView : public View
{
public:
ModalMessageView(
NavigationView &nav,
const std::string &title,
const std::string &message,
const modal_t type,
const std::function<void(bool)> on_choice);
std::string title() const override { return title_; };
void paint(Painter &painter) override;
void focus() override;
private:
const std::string title_;
const std::string message_;
const modal_t type_;
const std::function<void(bool)> on_choice_;
std::string title() const override { return title_; };
Button button_ok {
{ 10 * 8, 14 * 16, 10 * 8, 48 },
"OK",
private:
const std::string title_;
const std::string message_;
const modal_t type_;
const std::function<void(bool)> on_choice_;
Button button_ok{
{10 * 8, 14 * 16, 10 * 8, 48},
"OK",
};
Button button_yes{
{5 * 8, 14 * 16, 8 * 8, 48},
"YES",
};
Button button_no{
{17 * 8, 14 * 16, 8 * 8, 48},
"NO",
};
};
Button button_yes {
{ 5 * 8, 14 * 16, 8 * 8, 48 },
"YES",
};
Button button_no {
{ 17 * 8, 14 * 16, 8 * 8, 48 },
"NO",
};
};
} /* namespace ui */
#endif/*__UI_NAVIGATION_H__*/
#endif /*__UI_NAVIGATION_H__*/
+11
View File
@@ -113,6 +113,8 @@ set(CPPSRC
baseband_stats_collector.cpp
dsp_decimate.cpp
dsp_demodulate.cpp
dsp_hilbert.cpp
dsp_modulate.cpp
dsp_goertzel.cpp
matched_filter.cpp
spectrum_collector.cpp
@@ -125,6 +127,7 @@ set(CPPSRC
${COMMON}/dsp_fft.cpp
${COMMON}/dsp_fir_taps.cpp
${COMMON}/dsp_iir.cpp
${COMMON}/dsp_sos.cpp
fxpt_atan2.cpp
rssi.cpp
rssi_dma.cpp
@@ -312,6 +315,14 @@ set(MODE_CPPSRC
proc_afskrx.cpp
)
DeclareTargets(PAFR afskrx)
### APRS RX
set(MODE_CPPSRC
proc_aprsrx.cpp
)
DeclareTargets(PAPR aprsrx)
### NRF RX
set(MODE_CPPSRC
@@ -35,7 +35,7 @@ class ChannelStatsCollector {
public:
template<typename Callback>
void feed(const buffer_c16_t& src, Callback callback) {
auto src_p = src.p;
void *src_p = src.p;
while(src_p < &src.p[src.count]) {
const uint32_t sample = *__SIMD32(src_p)++;
const uint32_t mag_sq = __SMUAD(sample, sample);
+57
View File
@@ -0,0 +1,57 @@
/*
* Copyright (C) 2020 Belousov Oleg
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "dsp_hilbert.hpp"
#include "dsp_sos_config.hpp"
namespace dsp {
HilbertTransform::HilbertTransform() {
n = 0;
sos_i.configure(half_band_lpf_config);
sos_q.configure(half_band_lpf_config);
}
void HilbertTransform::execute(float in, float &out_i, float &out_q) {
float a = 0, b = 0;
switch (n) {
case 0: a = in; b = 0; break;
case 1: a = 0; b = -in; break;
case 2: a = -in; b = 0; break;
case 3: a = 0; b = in; break;
}
float i = sos_i.execute(a) * 2.0f;
float q = sos_q.execute(b) * 2.0f;
switch (n) {
case 0: out_i = i; out_q = q; break;
case 1: out_i = -q; out_q = i; break;
case 2: out_i = -i; out_q = -q; break;
case 3: out_i = q; out_q = -i; break;
}
n = (n + 1) % 4;
}
} /* namespace dsp */
+44
View File
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2020 Belousov Oleg
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __DSP_HILBERT_H__
#define __DSP_HILBERT_H__
#include "dsp_sos.hpp"
#include "dsp_types.hpp"
namespace dsp {
class HilbertTransform {
public:
HilbertTransform();
void execute(float in, float &out_i, float &out_q);
private:
uint8_t n = 0;
SOSFilter<5> sos_i;
SOSFilter<5> sos_q;
};
} /* namespace dsp */
#endif/*__DSP_HILBERT_H__*/
+137
View File
@@ -0,0 +1,137 @@
/*
* Copyright (C) 2020 Belousov Oleg
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "dsp_modulate.hpp"
#include "sine_table_int8.hpp"
namespace dsp {
namespace modulate {
Modulator::~Modulator() {
}
Mode Modulator::get_mode() {
return mode;
}
void Modulator::set_mode(Mode new_mode) {
mode = new_mode;
}
void Modulator::set_over(uint32_t new_over) {
over = new_over;
}
///
SSB::SSB() : hilbert() {
mode = Mode::LSB;
}
void SSB::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer) {
int32_t sample = 0;
int8_t re = 0, im = 0;
for (size_t counter = 0; counter < buffer.count; counter++) {
if (counter % 128 == 0) {
float i = 0.0, q = 0.0;
sample = audio.p[counter / over] >> 2;
//switch (mode) {
//case Mode::LSB:
hilbert.execute(sample / 32768.0f, i, q);
//case Mode::USB: hilbert.execute(sample / 32768.0f, q, i);
//default: break;
//}
i *= 64.0f;
q *= 64.0f;
switch (mode) {
case Mode::LSB: re = q; im = i; break;
case Mode::USB: re = i; im = q; break;
default: re = 0; im = 0; break;
}
//re = q;
//im = i;
//break;
}
buffer.p[counter] = { re, im };
}
}
///
FM::FM() {
mode = Mode::FM;
}
void FM::set_fm_delta(uint32_t new_delta) {
fm_delta = new_delta;
}
void FM::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer) {
int32_t sample = 0;
int8_t re, im;
for (size_t counter = 0; counter < buffer.count; counter++) {
if (counter % over == 0) {
sample = audio.p[counter / over] >> 8;
delta = sample * fm_delta;
}
phase += delta;
sphase = phase >> 24;
re = (sine_table_i8[(sphase + 64) & 255]);
im = (sine_table_i8[sphase]);
buffer.p[counter] = { re, im };
}
}
AM::AM() {
mode = Mode::AM;
}
void AM::execute(const buffer_s16_t& audio, const buffer_c8_t& buffer) {
int32_t sample = 0;
int8_t re = 0, im = 0;
float q = 0.0;
for (size_t counter = 0; counter < buffer.count; counter++) {
if (counter % 128 == 0) {
sample = audio.p[counter / over] >> 2;
}
q = sample / 32768.0f;
q *= 64.0f;
switch (mode) {
case Mode::AM: re = q + 20; im = q + 20; break;
case Mode::DSB: re = q; im = q; break;
default: break;
}
buffer.p[counter] = { re, im };
}
}
}
}
+96
View File
@@ -0,0 +1,96 @@
/*
* Copyright (C) 2020 Belousov Oleg
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __DSP_MODULATE_H__
#define __DSP_MODULATE_H__
#include "dsp_types.hpp"
#include "dsp_hilbert.hpp"
namespace dsp {
namespace modulate {
enum class Mode {
None,
AM,
DSB,
LSB,
USB,
FM
};
///
class Modulator {
public:
virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer) = 0;
virtual ~Modulator();
Mode get_mode();
void set_mode(Mode new_mode);
void set_over(uint32_t new_over);
protected:
uint32_t over = 1;
Mode mode = Mode::None;
};
///
class SSB : public Modulator {
public:
SSB();
virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer);
private:
dsp::HilbertTransform hilbert;
};
///
class FM : public Modulator {
public:
FM();
virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer);
void set_fm_delta(uint32_t new_delta);
///
private:
uint32_t fm_delta { 0 };
uint32_t phase { 0 }, sphase { 0 };
int32_t sample { 0 }, delta { };
};
class AM : public Modulator {
public:
AM();
virtual void execute(const buffer_s16_t& audio, const buffer_c8_t& buffer);
};
} /* namespace modulate */
} /* namespace dsp */
#endif/*__DSP_MODULATE_H__*/
+92 -80
View File
@@ -32,115 +32,125 @@ using namespace adsb;
void ADSBRXProcessor::execute(const buffer_c8_t& buffer) {
int8_t re, im;
float mag;
uint32_t mag;
uint32_t c;
uint8_t level, bit, byte { };
//bool confidence;
bool first_in_window, last_in_window;
uint8_t bit, byte{};
// This is called at 2M/2048 = 977Hz
// One pulse = 500ns = 2 samples
// One bit = 2 pulses = 1us = 4 samples
if (!configured) return;
for (size_t i = 0; i < buffer.count; i++) {
// Compute sample's magnitude
re = buffer.p[i].real();
im = buffer.p[i].imag();
mag = __builtin_sqrtf((re * re) + (im * im)) * k;
// Only used for preamble detection and visualisation
level = (mag < 0.3) ? 0 : // Blank weak signals
(mag > prev_mag) ? 1 : 0;
re = (int32_t)buffer.p[i].real(); // make re float and scale it
im = (int32_t)buffer.p[i].imag(); // make re float and scale it
mag = ((uint32_t)(re*re) + (uint32_t)(im*im));
if (decoding) {
// Decode
// 1 bit lasts 2 samples
if (sample_count & 1) {
if ((prev_mag < threshold_low) && (mag < threshold_low)) {
// Both under window, silence.
if (null_count > 3) {
const ADSBFrameMessage message(frame);
shared_memory.application_queue.push(message);
decoding = false;
} else
null_count++;
//confidence = false;
if (prev_mag > mag)
bit = 1;
else
bit = 0;
} else {
null_count = 0;
first_in_window = ((prev_mag >= threshold_low) && (prev_mag <= threshold_high));
last_in_window = ((mag >= threshold_low) && (mag <= threshold_high));
if ((first_in_window && !last_in_window) || (!first_in_window && last_in_window)) {
//confidence = true;
if (prev_mag > mag)
bit = 1;
else
bit = 0;
} else {
//confidence = false;
if (prev_mag > mag)
bit = 1;
else
bit = 0;
}
if (bit_count >= msgLen)
{
const ADSBFrameMessage message(frame, amp);
shared_memory.application_queue.push(message);
decoding = false;
bit = (prev_mag > mag) ? 1 : 0;
}
else
{
//confidence = true;
bit = (prev_mag > mag) ? 1 : 0;
}
byte = bit | (byte << 1);
bit_count++;
// Perform checks at the end of the first byte
if (!(bit_count & 7)) {
// Got one byte
// Store the byte
frame.push_byte(byte);
}
}
// Check at the end of the first byte of the message
uint8_t df = (byte >> 3);
if ( (bit_count == 8) && !(df & 0x10) ) {
msgLen = 56; // DFs 16 or greater are long 112. DFs 15 or less are short 56.
}
// Abondon all frames that arent DF17 or DF18 extended squitters
if ( (bit_count == 8) && !((df == 17)||(df == 18)) ) {
decoding = false;
bit = (prev_mag > mag) ? 1 : 0;
frame.clear();
}
} // last bit of a byte
} // Second sample of each bit
sample_count++;
} else {
// Look for preamble
// Shift
for (c = 0; c < (ADSB_PREAMBLE_LENGTH - 1); c++)
shifter[c] = shifter[c + 1];
shifter[15] = std::make_pair(mag, level);
// Compare
for (c = 0; c < ADSB_PREAMBLE_LENGTH; c++) {
if (shifter[c].second != adsb_preamble[c])
break;
}
if (c == ADSB_PREAMBLE_LENGTH) {
decoding = true;
sample_count = 0;
null_count = 0;
bit_count = 0;
frame.clear();
// Compute preamble pulses power to set thresholds
threshold = (shifter[0].first + shifter[2].first + shifter[7].first + shifter[9].first) / 4;
threshold_high = threshold * 1.414; // +3dB
threshold_low = threshold * 0.707; // -3dB
}
}
// Continue looking for preamble even if in a packet
// switch is new preamble id higher magnitude
// Shift the preamble
for (c = 0; c < (ADSB_PREAMBLE_LENGTH ); c++) { shifter[c] = shifter[c + 1]; }
shifter[ADSB_PREAMBLE_LENGTH] = mag;
// First check of relations between the first 10 samples
// representing a valid preamble. We don't even investigate further
// if this simple test is not passed
if (shifter[0] < shifter[1] &&
shifter[1] > shifter[2] &&
shifter[2] < shifter[3] &&
shifter[3] > shifter[4] &&
shifter[4] < shifter[1] &&
shifter[5] < shifter[1] &&
shifter[6] < shifter[1] &&
shifter[7] < shifter[1] &&
shifter[8] > shifter[9] &&
shifter[9] < shifter[10] &&
shifter[10]> shifter[11] )
{
// The samples between the two spikes must be < than the average
// of the high spikes level. We don't test bits too near to
// the high levels as signals can be out of phase so part of the
// energy can be in the near samples
int32_t thisAmp = (shifter[1] + shifter[3] + shifter[8] + shifter[10]);
int32_t high = thisAmp / 9;
if (
shifter[5] < high &&
shifter[6] < high &&
// Similarly samples in the range 11-13 must be low, as it is the
// space between the preamble and real data. Again we don't test
// bits too near to high levels, see above
shifter[12] < high &&
shifter[13] < high &&
shifter[14] < high )
{
if ((decoding == false) || // New preamble
((decoding == true) && (thisAmp > amp))) // Higher power than existing packet
{
decoding = true;
msgLen = 112;
amp = thisAmp;
sample_count = 0;
bit_count = 0;
frame.clear();
}
} // 4 & 5 low and 11-14 low
} // Check for preamble pattern
// Store mag for next time
prev_mag = mag;
}
}
void ADSBRXProcessor::on_message(const Message* const message) {
if (message->id == Message::ID::ADSBConfigure) {
null_count = 0;
bit_count = 0;
sample_count = 0;
decoding = false;
@@ -148,8 +158,10 @@ void ADSBRXProcessor::on_message(const Message* const message) {
}
}
#ifndef _WIN32
int main() {
EventDispatcher event_dispatcher { std::make_unique<ADSBRXProcessor>() };
event_dispatcher.run();
return 0;
}
#endif
+6 -7
View File
@@ -40,8 +40,6 @@ public:
void on_message(const Message* const message) override;
private:
static constexpr float k = 1.0f / 128.0f;
static constexpr size_t baseband_fs = 2000000;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
@@ -49,16 +47,17 @@ private:
ADSBFrame frame { };
bool configured { false };
float prev_mag { 0 };
float threshold { }, threshold_low { }, threshold_high { };
size_t null_count { 0 }, bit_count { 0 }, sample_count { 0 };
std::pair<float, uint8_t> shifter[ADSB_PREAMBLE_LENGTH];
uint32_t prev_mag { 0 };
size_t bit_count { 0 }, sample_count { 0 };
size_t msgLen{ 112 };
uint32_t shifter[ADSB_PREAMBLE_LENGTH+1];
bool decoding { };
bool preamble { }, active { };
uint16_t bit_pos { 0 };
uint8_t cur_bit { 0 };
uint32_t sample { 0 };
int8_t re { }, im { };
int32_t re { }, im { };
int32_t amp {0};
};
#endif
+5 -4
View File
@@ -35,7 +35,7 @@ void NarrowbandAMAudio::execute(const buffer_c8_t& buffer) {
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
channel_spectrum.feed(decim_1_out, channel_filter_pass_f, channel_filter_stop_f);
channel_spectrum.feed(decim_1_out, channel_filter_low_f, channel_filter_high_f, channel_filter_transition);
const auto decim_2_out = decim_2.execute(decim_1_out, dst_buffer);
const auto channel_out = channel_filter.execute(decim_2_out, dst_buffer);
@@ -87,14 +87,15 @@ void NarrowbandAMAudio::configure(const AMConfigureMessage& message) {
constexpr size_t decim_2_output_fs = decim_2_input_fs / decim_2_decimation_factor;
constexpr size_t channel_filter_input_fs = decim_2_output_fs;
const size_t channel_filter_output_fs = channel_filter_input_fs / channel_filter_decimation_factor;
//const size_t channel_filter_output_fs = channel_filter_input_fs / channel_filter_decimation_factor;
decim_0.configure(message.decim_0_filter.taps, 33554432);
decim_1.configure(message.decim_1_filter.taps, 131072);
decim_2.configure(message.decim_2_filter.taps, decim_2_decimation_factor);
channel_filter.configure(message.channel_filter.taps, channel_filter_decimation_factor);
channel_filter_pass_f = message.channel_filter.pass_frequency_normalized * channel_filter_input_fs;
channel_filter_stop_f = message.channel_filter.stop_frequency_normalized * channel_filter_input_fs;
channel_filter_low_f = message.channel_filter.low_frequency_normalized * channel_filter_input_fs;
channel_filter_high_f = message.channel_filter.high_frequency_normalized * channel_filter_input_fs;
channel_filter_transition = message.channel_filter.transition_normalized * channel_filter_input_fs;
channel_spectrum.set_decimation_factor(1.0f);
modulation_ssb = (message.modulation == AMConfigureMessage::Modulation::SSB);
audio_output.configure(message.audio_hpf_config);
+3 -2
View File
@@ -64,8 +64,9 @@ private:
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
dsp::decimate::FIRAndDecimateComplex decim_2 { };
dsp::decimate::FIRAndDecimateComplex channel_filter { };
uint32_t channel_filter_pass_f = 0;
uint32_t channel_filter_stop_f = 0;
int32_t channel_filter_low_f = 0;
int32_t channel_filter_high_f = 0;
int32_t channel_filter_transition = 0;
bool modulation_ssb = false;
dsp::demodulate::AM demod_am { };
+6 -4
View File
@@ -43,14 +43,15 @@ void WidebandFMAudio::execute(const buffer_c8_t& buffer) {
const buffer_c16_t buffer_c16 {spectrum.data(),spectrum.size(),buffer.sampling_rate};
channel_spectrum.feed(buffer_c16);
int8_t re, im;
int8_t mag;
int8_t re ;
//int8_t im;
//int8_t mag;
for (size_t i = 0; i < 128; i++)
{
re = buffer.p[i].real();
im = buffer.p[i].imag();
mag = __builtin_sqrtf((re * re) + (im * im)) ;
//im = buffer.p[i].imag();
//mag = __builtin_sqrtf((re * re) + (im * im)) ;
const unsigned int v = re + 127.0f; //timescope
audio_spectrum.db[i] = std::max(0U, std::min(255U, v));
}
@@ -75,6 +76,7 @@ void WidebandFMAudio::on_message(const Message* const message) {
}
void WidebandFMAudio::configure(const WFMConfigureMessage& message) {
(void)message; // avoid warning
configured = true;
}
+257
View File
@@ -0,0 +1,257 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "proc_aprsrx.hpp"
#include "portapack_shared_memory.hpp"
#include "event_m4.hpp"
#include "stdio.h"
void APRSRxProcessor::execute(const buffer_c8_t& buffer) {
// This is called at 3072000 / 2048 = 1500Hz
if (!configured) return;
// FM demodulation
const auto decim_0_out = decim_0.execute(buffer, dst_buffer); // 2048 / 8 = 256 (512 I/Q samples)
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); // 256 / 8 = 32 (64 I/Q samples)
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer); // 32 / 2 = 16 (32 I/Q samples)
feed_channel_stats(channel_out);
auto audio = demod.execute(channel_out, audio_buffer);
audio_output.write(audio);
// Audio signal processing
for (size_t c = 0; c < audio.count; c++) {
const int32_t sample_int = audio.p[c] * 32768.0f;
int32_t current_sample = __SSAT(sample_int, 16);
current_sample /= 128;
// Delay line put
delay_line[delay_line_index & 0x3F] = current_sample;
// Delay line get, and LPF
sample_mixed = (delay_line[(delay_line_index - (samples_per_bit/2)) & 0x3F] * current_sample) / 4;
sample_filtered = prev_mixed + sample_mixed + (prev_filtered / 2);
delay_line_index++;
prev_filtered = sample_filtered;
prev_mixed = sample_mixed;
// Slice
sample_bits <<= 1;
uint8_t bit = (sample_filtered < -20) ? 1 : 0;
sample_bits |= bit;
/*
int16_t scaled = bit == 1 ? 32767 : -32767;
if( stream ) {
const size_t bytes_to_write = sizeof(scaled) * 1;
const auto result = stream->write(&scaled, bytes_to_write);
}
*/
// Check for "clean" transition: either 0011 or 1100
if ((((sample_bits >> 2) ^ sample_bits) & 3) == 3) {
// Adjust phase
if (phase < 0x8000)
phase += 0x800; // Is this a proper value ?
else
phase -= 0x800;
}
phase += phase_inc;
if (phase >= 0x10000) {
phase &= 0xFFFF;
if (true) {
uint8_t bit;
if(__builtin_popcount(sample_bits & 0xFF) >= 0x05){
bit = 0x1;
}
else {
bit = 0x0;
}
if(parse_bit(bit)){
parse_packet();
}
}
}
}
}
void APRSRxProcessor::parse_packet(){
//validate crc
if(packet_buffer_size >= aprs::APRS_MIN_LENGTH){
uint16_t crc = 0xFFFF;
for(size_t i = 0; i < packet_buffer_size; i++){
uint8_t byte = packet_buffer[i];
crc = ((crc >> 8) ^ crc_ccitt_tab[(crc ^ byte) & 0xFF]) & 0xFFFF;
}
if(crc == 0xF0B8){
parse_ax25();
}
}
}
void APRSRxProcessor::parse_ax25(){
aprs_packet.clear();
aprs_packet.set_valid_checksum(true);
for(size_t i = 0; i < packet_buffer_size; i++){
aprs_packet.set(i, packet_buffer[i]);
}
APRSPacketMessage packet_message { aprs_packet };
shared_memory.application_queue.push(packet_message);
}
bool APRSRxProcessor::parse_bit(const uint8_t current_bit){
uint8_t decoded_bit = ~(current_bit ^ last_bit) & 0x1;
last_bit = current_bit;
//int16_t log = decoded_bit == 0 ? -32768 : 32767;
//if(stream){
// const size_t bytes_to_write = sizeof(log) * 1;
// const auto result = stream->write(&log, bytes_to_write);
//}
if(decoded_bit & 0x1){
if(ones_count < 8){
ones_count++;
}
}
else {
if(ones_count > 6){ //not valid
state = WAIT_FLAG;
current_byte = 0;
ones_count = 0;
byte_index = 0;
packet_buffer_size = 0;
return false;
}
else if(ones_count == 6){ //flag
bool done = false;
if(state == IN_FRAME){
done = true;
}
else {
packet_buffer_size = 0;
}
state = WAIT_FRAME;
current_byte = 0;
ones_count = 0;
byte_index = 0;
return done;
}
else if(ones_count == 5){ //bit stuff
ones_count = 0;
return false;
}
else {
ones_count = 0;
}
}
//store
current_byte = current_byte >> 1;
current_byte |= (decoded_bit == 0x1 ? 0x80 : 0x0);
byte_index++;
if(byte_index >= 8){
byte_index = 0;
if(state == WAIT_FRAME){
state = IN_FRAME;
}
if(state == IN_FRAME){
if(packet_buffer_size + 1 >= 256){
state = WAIT_FLAG;
current_byte = 0;
ones_count = 0;
byte_index = 0;
packet_buffer_size = 0;
return false;
}
packet_buffer[packet_buffer_size++] = current_byte;
}
}
return false;
}
void APRSRxProcessor::on_message(const Message* const message) {
if (message->id == Message::ID::APRSRxConfigure)
configure(*reinterpret_cast<const APRSRxConfigureMessage*>(message));
if(message->id == Message::ID::CaptureConfig)
capture_config(*reinterpret_cast<const CaptureConfigMessage*>(message));
}
void APRSRxProcessor::capture_config(const CaptureConfigMessage& message) {
if( message.config ) {
//stream = std::make_unique<StreamInput>(message.config);
audio_output.set_stream(std::make_unique<StreamInput>(message.config));
} else {
//stream.reset();
audio_output.set_stream(nullptr);
}
}
void APRSRxProcessor::configure(const APRSRxConfigureMessage& message) {
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
decim_1.configure(taps_11k0_decim_1.taps, 131072);
channel_filter.configure(taps_11k0_channel.taps, 2);
demod.configure(audio_fs, 5000);
audio_output.configure(audio_24k_hpf_300hz_config, audio_24k_deemph_300_6_config, 0);
samples_per_bit = audio_fs / message.baudrate;
phase_inc = (0x10000 * message.baudrate) / audio_fs;
phase = 0;
// Delay line
delay_line_index = 0;
state = WAIT_FLAG;
configured = true;
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<APRSRxProcessor>() };
event_dispatcher.run();
return 0;
}
+148
View File
@@ -0,0 +1,148 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __PROC_APRSRX_H__
#define __PROC_APRSRX_H__
#include "baseband_processor.hpp"
#include "baseband_thread.hpp"
#include "rssi_thread.hpp"
#include "dsp_decimate.hpp"
#include "dsp_demodulate.hpp"
#include "stream_input.hpp"
#include "audio_output.hpp"
#include "fifo.hpp"
#include "message.hpp"
#include "aprs_packet.hpp"
static uint16_t crc_ccitt_tab[256] = {
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
};
class APRSRxProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
private:
static constexpr size_t baseband_fs = 3072000;
static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2;
size_t samples_per_bit { };
enum State {
WAIT_FLAG,
WAIT_FRAME,
IN_FRAME
};
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
RSSIThread rssi_thread { NORMALPRIO + 10 };
std::array<complex16_t, 512> dst { };
const buffer_c16_t dst_buffer {
dst.data(),
dst.size()
};
std::array<float, 32> audio { };
const buffer_f32_t audio_buffer {
audio.data(),
audio.size()
};
// Array size ok down to 375 bauds (24000 / 375)
std::array<int32_t, 64> delay_line { 0 };
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
dsp::decimate::FIRAndDecimateComplex channel_filter { };
std::unique_ptr<StreamInput> stream { };
dsp::demodulate::FM demod { };
AudioOutput audio_output { };
State state { };
size_t delay_line_index { };
uint32_t bit_counter { 0 };
uint32_t word_bits { 0 };
uint32_t sample_bits { 0 };
uint32_t phase { }, phase_inc { };
int32_t sample_mixed { }, prev_mixed { }, sample_filtered { }, prev_filtered { };
uint8_t last_bit;
uint8_t ones_count = 0;
uint8_t current_byte = 0;
uint8_t byte_index = 0;
uint8_t packet_buffer[256];
size_t packet_buffer_size = 0;
bool configured { false };
bool wait_start { };
bool bit_value { };
aprs::APRSPacket aprs_packet;
void configure(const APRSRxConfigureMessage& message);
void capture_config(const CaptureConfigMessage& message);
void parse_packet();
bool parse_bit(const uint8_t bit);
void parse_ax25();
};
#endif/*__PROC_TPMS_H__*/
+1 -1
View File
@@ -50,7 +50,7 @@ private:
int32_t sample { 0 }, delta { };
int8_t re { 0 }, im { 0 };
size_t progress_interval_samples, progress_samples = 0;
size_t progress_interval_samples = 0 , progress_samples = 0;
bool configured { false };
uint32_t bytes_read { 0 };
+7 -7
View File
@@ -53,7 +53,6 @@ void BTLERxProcessor::execute(const buffer_c8_t& buffer) {
auto audio_oversampled = demod.execute(buffer_c16, work_audio_buffer);*/
// Audio signal processing
for (size_t c = 0; c < audio_oversampled.count; c++) {
int result;
/*const int32_t sample_int = audio_oversampled.p[c] * 32768.0f;
int32_t current_sample = __SSAT(sample_int, 16);
@@ -107,9 +106,9 @@ void BTLERxProcessor::execute(const buffer_c8_t& buffer) {
uint8_t packet_data[500];
int packet_length;
uint32_t packet_crc;
uint32_t calced_crc;
//uint32_t calced_crc; // NOTE: restore when CRC is passing
uint64_t packet_addr_l;
uint32_t result;
//uint32_t result; // NOTE: restore when CRC is passing
uint8_t crc[3];
uint8_t packet_header_arr[2];
@@ -255,21 +254,21 @@ void BTLERxProcessor::execute(const buffer_c8_t& buffer) {
counter = counter + 1;
}
for (v=0;v<3;v++) crc_result=(crc_result<<8)|crc[v];
calced_crc = crc_result;
//calced_crc = crc_result; // NOTE: restore when CRC is passing
packet_crc=0;
for (int c=0;c<3;c++) packet_crc=(packet_crc<<8)|packet_data[packet_length+2+c];
if (packet_addr_l==0x8E89BED6)
//if (packet_crc==calced_crc)
//if (packet_crc==calced_crc) // NOTE: restore when CRC is passing
{
uint8_t mac_data[6];
int counter = 0;
for (int i = 7; i >= 2; i--)
{
uint8_t byte_temp6 = (uint8_t) (((packet_data[i] * 0x0802LU & 0x22110LU) | (packet_data[i] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
//result = byte_temp6;
mac_data[counter] = byte_temp6;
//result = byte_temp6; // NOTE: restore when CRC is passing
mac_data[counter] = byte_temp6;
counter = counter + 1;
}
@@ -325,6 +324,7 @@ void BTLERxProcessor::on_message(const Message* const message) {
}
void BTLERxProcessor::configure(const BTLERxConfigureMessage& message) {
(void)message; //avoid warning
decim_0.configure(taps_200k_wfm_decim_0.taps, 33554432);
decim_1.configure(taps_200k_wfm_decim_1.taps, 131072);
demod.configure(audio_fs, 5000);
+9 -4
View File
@@ -44,7 +44,11 @@ void CaptureProcessor::execute(const buffer_c8_t& buffer) {
if( stream ) {
const size_t bytes_to_write = sizeof(*decimator_out.p) * decimator_out.count;
const auto result = stream->write(decimator_out.p, bytes_to_write);
const size_t written = stream->write(decimator_out.p, bytes_to_write);
if( written != bytes_to_write )
{
//TODO eventually report error somewhere
}
}
feed_channel_stats(channel);
@@ -52,7 +56,7 @@ void CaptureProcessor::execute(const buffer_c8_t& buffer) {
spectrum_samples += channel.count;
if( spectrum_samples >= spectrum_interval_samples ) {
spectrum_samples -= spectrum_interval_samples;
channel_spectrum.feed(channel, channel_filter_pass_f, channel_filter_stop_f);
channel_spectrum.feed(channel, channel_filter_low_f, channel_filter_high_f, channel_filter_transition);
}
}
@@ -85,8 +89,9 @@ void CaptureProcessor::samplerate_config(const SamplerateConfigMessage& message)
size_t decim_1_input_fs = decim_0_output_fs;
size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
channel_filter_pass_f = taps_200k_decim_1.pass_frequency_normalized * decim_1_input_fs; // 162760.416666667
channel_filter_stop_f = taps_200k_decim_1.stop_frequency_normalized * decim_1_input_fs; // 337239.583333333
channel_filter_low_f = taps_200k_decim_1.low_frequency_normalized * decim_1_input_fs;
channel_filter_high_f = taps_200k_decim_1.high_frequency_normalized * decim_1_input_fs;
channel_filter_transition = taps_200k_decim_1.transition_normalized * decim_1_input_fs;
spectrum_interval_samples = decim_1_output_fs / spectrum_rate_hz;
spectrum_samples = 0;
+3 -2
View File
@@ -60,8 +60,9 @@ private:
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { };
dsp::decimate::FIRC16xR16x16Decim2 decim_1 { };
uint32_t channel_filter_pass_f = 0;
uint32_t channel_filter_stop_f = 0;
int32_t channel_filter_low_f = 0;
int32_t channel_filter_high_f = 0;
int32_t channel_filter_transition = 0;
std::unique_ptr<StreamInput> stream { };
+3 -2
View File
@@ -30,8 +30,9 @@
#include "utility.hpp"
ReplayProcessor::ReplayProcessor() {
channel_filter_pass_f = taps_200k_decim_1.pass_frequency_normalized * 1000000; // 162760.416666667
channel_filter_stop_f = taps_200k_decim_1.stop_frequency_normalized * 1000000; // 337239.583333333
channel_filter_low_f = taps_200k_decim_1.low_frequency_normalized * 1000000;
channel_filter_high_f = taps_200k_decim_1.high_frequency_normalized * 1000000;
channel_filter_transition = taps_200k_decim_1.transition_normalized * 1000000;
spectrum_samples = 0;
+3 -2
View File
@@ -55,8 +55,9 @@ private:
baseband_fs
};
uint32_t channel_filter_pass_f = 0;
uint32_t channel_filter_stop_f = 0;
int32_t channel_filter_low_f = 0;
int32_t channel_filter_high_f = 0;
int32_t channel_filter_transition = 0;
std::unique_ptr<StreamOutput> stream { };
+37 -1
View File
@@ -35,6 +35,7 @@ void MicTXProcessor::execute(const buffer_c8_t& buffer){
if (!configured) return;
audio_input.read_audio_buffer(audio_buffer);
modulator->execute(audio_buffer, buffer);
for (size_t i = 0; i < buffer.count; i++) {
@@ -70,6 +71,7 @@ void MicTXProcessor::execute(const buffer_c8_t& buffer){
sample = beep_gen.process(0);
}
/*
sample = tone_gen.process(sample);
// FM
@@ -87,6 +89,7 @@ void MicTXProcessor::execute(const buffer_c8_t& buffer){
}
buffer.p[i] = { re, im };
*/
}
}
@@ -96,7 +99,40 @@ void MicTXProcessor::on_message(const Message* const msg) {
switch(msg->id) {
case Message::ID::AudioTXConfig:
fm_delta = config_message.deviation_hz * (0xFFFFFFUL / baseband_fs);
if (fm_enabled) {
dsp::modulate::FM *fm = new dsp::modulate::FM();
fm->set_fm_delta(config_message.deviation_hz * (0xFFFFFFUL / baseband_fs));
modulator = fm;
}
if (usb_enabled) {
modulator = new dsp::modulate::SSB();
modulator->set_mode(dsp::modulate::Mode::USB);
}
if (lsb_enabled) {
modulator = new dsp::modulate::SSB();
modulator->set_mode(dsp::modulate::Mode::LSB);
}
if (am_enabled) {
modulator = new dsp::modulate::AM();
modulator->set_mode(dsp::modulate::Mode::AM);
}
if (dsb_enabled) {
modulator = new dsp::modulate::AM();
modulator->set_mode(dsp::modulate::Mode::DSB);
}
modulator->set_over(baseband_fs / 24000);
am_enabled = config_message.am_enabled;
usb_enabled = config_message.usb_enabled;
lsb_enabled = config_message.lsb_enabled;
dsb_enabled = config_message.dsb_enabled;
if (!am_enabled || !usb_enabled || !lsb_enabled || !dsb_enabled) {
fm_enabled = true;
}
audio_gain = config_message.audio_gain;
divider = config_message.divider;
+9 -1
View File
@@ -27,6 +27,7 @@
#include "baseband_thread.hpp"
#include "audio_input.hpp"
#include "tone_gen.hpp"
#include "dsp_modulate.hpp"
class MicTXProcessor : public BasebandProcessor {
public:
@@ -50,7 +51,14 @@ private:
AudioInput audio_input { };
ToneGen tone_gen { };
ToneGen beep_gen { };
dsp::modulate::Modulator *modulator;
bool am_enabled { false };
bool fm_enabled { true };
bool usb_enabled { false };
bool lsb_enabled { false };
bool dsb_enabled { false };
uint32_t divider { };
float audio_gain { };
uint64_t power_acc { 0 };
+4 -3
View File
@@ -39,7 +39,7 @@ void NarrowbandFMAudio::execute(const buffer_c8_t& buffer) {
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
channel_spectrum.feed(decim_1_out, channel_filter_pass_f, channel_filter_stop_f);
channel_spectrum.feed(decim_1_out, channel_filter_low_f, channel_filter_high_f, channel_filter_transition);
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer);
@@ -145,8 +145,9 @@ void NarrowbandFMAudio::configure(const NBFMConfigureMessage& message) {
decim_1.configure(message.decim_1_filter.taps, 131072);
channel_filter.configure(message.channel_filter.taps, message.channel_decimation);
demod.configure(demod_input_fs, message.deviation);
channel_filter_pass_f = message.channel_filter.pass_frequency_normalized * channel_filter_input_fs;
channel_filter_stop_f = message.channel_filter.stop_frequency_normalized * channel_filter_input_fs;
channel_filter_low_f = message.channel_filter.low_frequency_normalized * channel_filter_input_fs;
channel_filter_high_f = message.channel_filter.high_frequency_normalized * channel_filter_input_fs;
channel_filter_transition = message.channel_filter.transition_normalized * channel_filter_input_fs;
channel_spectrum.set_decimation_factor(1.0f);
audio_output.configure(message.audio_hpf_config, message.audio_deemph_config, (float)message.squelch_level / 100.0);
+3 -2
View File
@@ -73,8 +73,9 @@ private:
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
dsp::decimate::FIRAndDecimateComplex channel_filter { };
uint32_t channel_filter_pass_f = 0;
uint32_t channel_filter_stop_f = 0;
int32_t channel_filter_low_f = 0;
int32_t channel_filter_high_f = 0;
int32_t channel_filter_transition = 0;
// For CTCSS decoding
dsp::decimate::FIR64AndDecimateBy2Real ctcss_filter { };
+1 -4
View File
@@ -37,7 +37,6 @@ void NRFRxProcessor::execute(const buffer_c8_t& buffer) {
auto audio_oversampled = demod.execute(decim_0_out, work_audio_buffer);
// Audio signal processing
for (size_t c = 0; c < audio_oversampled.count; c++) {
int result;
int g_srate = 4; //4 for 250KPS
//int g_srate = 1; //1 for 1MPS, not working yet
int32_t current_sample = audio_oversampled.p[c]; //if I directly use this, some results can pass crc but not correct.
@@ -48,11 +47,8 @@ void NRFRxProcessor::execute(const buffer_c8_t& buffer) {
skipSamples = skipSamples - 1;
if (skipSamples<1)
{
int32_t threshold_tmp=0;
for (int c=0;c<8*g_srate;c++)
{
@@ -272,6 +268,7 @@ void NRFRxProcessor::on_message(const Message* const message) {
}
void NRFRxProcessor::configure(const NRFRxConfigureMessage& message) {
(void)message; //avoir unused warning
decim_0.configure(taps_200k_wfm_decim_0.taps, 33554432);
decim_1.configure(taps_200k_wfm_decim_1.taps, 131072);
demod.configure(audio_fs, 5000);
+464 -106
View File
@@ -28,6 +28,9 @@
#include <cstdint>
#include <cstddef>
#include <algorithm> // std::max
#include <cmath>
void POCSAGProcessor::execute(const buffer_c8_t& buffer) {
// This is called at 1500Hz
@@ -39,114 +42,45 @@ void POCSAGProcessor::execute(const buffer_c8_t& buffer) {
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer);
auto audio = demod.execute(channel_out, audio_buffer);
//audio_output.write(audio);
smooth.Process(audio.p, audio.count); // Smooth the data to make decoding more accurate
audio_output.write(audio);
for (uint32_t c = 0; c < 16; c++) {
const int32_t sample_int = audio.p[c] * 32768.0f;
const int32_t audio_sample = __SSAT(sample_int, 16);
slicer_sr <<= 1;
if (phase == 0)
slicer_sr |= (audio_sample < 0); // Do we need hysteresis ?
else
slicer_sr |= !(audio_sample < 0);
// Detect transitions to adjust clock
if ((slicer_sr ^ (slicer_sr >> 1)) & 1) {
if (sphase < (0x8000u - sphase_delta_half))
sphase += sphase_delta_eighth;
else
sphase -= sphase_delta_eighth;
}
sphase += sphase_delta;
// Symbol time elapsed
if (sphase >= 0x10000u) {
sphase &= 0xFFFFu;
rx_data <<= 1;
rx_data |= (slicer_sr & 1);
switch (rx_state) {
case WAITING:
if (rx_data == 0xAAAAAAAA) {
rx_state = PREAMBLE;
sync_timeout = 0;
}
break;
case PREAMBLE:
if (sync_timeout < POCSAG_TIMEOUT) {
sync_timeout++;
processDemodulatedSamples(audio.p, 16);
extractFrames();
if (rx_data == POCSAG_SYNCWORD) {
packet.clear();
codeword_count = 0;
rx_bit = 0;
msg_timeout = 0;
rx_state = SYNC;
}
} else {
// Timeout here is normal (end of message)
rx_state = WAITING;
//push_packet(pocsag::PacketFlag::TIMED_OUT);
}
break;
case SYNC:
if (msg_timeout < POCSAG_BATCH_LENGTH) {
msg_timeout++;
rx_bit++;
if (rx_bit >= 32) {
rx_bit = 0;
// Got a complete codeword
//pocsag_brute_repair(&s->l2.pocsag, &rx_data);
packet.set(codeword_count, rx_data);
if (codeword_count < 15) {
codeword_count++;
} else {
push_packet(pocsag::PacketFlag::NORMAL);
rx_state = PREAMBLE;
sync_timeout = 0;
}
}
} else {
packet.set(0, codeword_count); // Replace first codeword with count, for debug
push_packet(pocsag::PacketFlag::TIMED_OUT);
rx_state = WAITING;
}
break;
default:
break;
}
}
}
}
void POCSAGProcessor::push_packet(pocsag::PacketFlag flag) {
packet.set_bitrate(bitrate);
packet.set_flag(flag);
packet.set_timestamp(Timestamp::now());
const POCSAGPacketMessage message(packet);
shared_memory.application_queue.push(message);
// ====================================================================
//
// ====================================================================
int POCSAGProcessor::OnDataWord(uint32_t word, int pos)
{
packet.set(pos, word);
return 0;
}
// ====================================================================
//
// ====================================================================
int POCSAGProcessor::OnDataFrame(int len, int baud)
{
if (len > 0)
{
packet.set_bitrate(baud);
packet.set_flag(pocsag::PacketFlag::NORMAL);
packet.set_timestamp(Timestamp::now());
const POCSAGPacketMessage message(packet);
shared_memory.application_queue.push(message);
}
return 0;
}
void POCSAGProcessor::on_message(const Message* const message) {
if (message->id == Message::ID::POCSAGConfigure)
configure(*reinterpret_cast<const POCSAGConfigureMessage*>(message));
configure();
}
void POCSAGProcessor::configure(const POCSAGConfigureMessage& message) {
void POCSAGProcessor::configure() {
constexpr size_t decim_0_input_fs = baseband_fs;
constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor;
@@ -162,18 +96,442 @@ void POCSAGProcessor::configure(const POCSAGConfigureMessage& message) {
decim_1.configure(taps_11k0_decim_1.taps, 131072);
channel_filter.configure(taps_11k0_channel.taps, 2);
demod.configure(demod_input_fs, 4500);
//audio_output.configure(false);
// Smoothing should be roughly sample rate over max baud
// 24k / 3.2k is 7.5
smooth.SetSize(8);
audio_output.configure(false);
bitrate = message.bitrate;
phase = message.phase;
sphase_delta = 0x10000u * bitrate / POCSAG_AUDIO_RATE;
sphase_delta_half = sphase_delta / 2; // Just for speed
sphase_delta_eighth = sphase_delta / 8;
rx_state = WAITING;
// Set up the frame extraction, limits of baud
setFrameExtractParams(demod_input_fs, 4000, 300, 32);
// Mark the class as ready to accept data
configured = true;
}
// -----------------------------
// Frame extractraction methods
// -----------------------------
#define BAUD_STABLE (104)
#define MAX_CONSEC_SAME (32)
#define MAX_WITHOUT_SINGLE (64)
#define MAX_BAD_TRANS (10)
#define M_SYNC (0x7cd215d8)
#define M_NOTSYNC (0x832dea27)
#define M_IDLE (0x7a89c197)
// ====================================================================
//
// ====================================================================
inline int bitsDiff(unsigned long left, unsigned long right)
{
unsigned long xord = left ^ right;
int count = 0;
for (int i = 0; i < 32; i++)
{
if ((xord & 0x01) != 0) ++count;
xord = xord >> 1;
}
return(count);
}
// ====================================================================
//
// ====================================================================
void POCSAGProcessor::initFrameExtraction()
{
m_averageSymbolLen_1024 = m_maxSymSamples_1024;
m_lastStableSymbolLen_1024 = m_minSymSamples_1024;
m_badTransitions = 0;
m_bitsStart = 0;
m_bitsEnd = 0;
m_inverted = false;
resetVals();
}
// ====================================================================
//
// ====================================================================
void POCSAGProcessor::resetVals()
{
// Reset the parameters
// --------------------
m_goodTransitions = 0;
m_badTransitions = 0;
m_averageSymbolLen_1024 = m_maxSymSamples_1024;
m_shortestGoodTrans_1024 = m_maxSymSamples_1024;
m_valMid = 0;
// And reset the counts
// --------------------
m_lastTransPos_1024 = 0;
m_lastBitPos_1024 = 0;
m_lastSample = 0;
m_sampleNo = 0;
m_nextBitPos_1024 = m_maxSymSamples_1024;
m_nextBitPosInt = (long)m_nextBitPos_1024;
// Extraction
m_fifo.numBits = 0;
m_gotSync = false;
m_numCode = 0;
}
// ====================================================================
//
// ====================================================================
void POCSAGProcessor::setFrameExtractParams(long a_samplesPerSec, long a_maxBaud, long a_minBaud, long maxRunOfSameValue)
{
m_samplesPerSec = a_samplesPerSec;
m_minSymSamples_1024 = (uint32_t)(1024.0f * (float)a_samplesPerSec / (float)a_maxBaud);
m_maxSymSamples_1024 = (uint32_t)(1024.0f*(float)a_samplesPerSec / (float)a_minBaud);
m_maxRunOfSameValue = maxRunOfSameValue;
m_shortestGoodTrans_1024 = m_maxSymSamples_1024;
m_averageSymbolLen_1024 = m_maxSymSamples_1024;
m_lastStableSymbolLen_1024 = m_minSymSamples_1024;
m_nextBitPos_1024 = m_averageSymbolLen_1024 / 2;
m_nextBitPosInt = m_nextBitPos_1024 >> 10;
initFrameExtraction();
}
// ====================================================================
//
// ====================================================================
int POCSAGProcessor::processDemodulatedSamples(float * sampleBuff, int noOfSamples)
{
bool transition = false;
uint32_t samplePos_1024 = 0;
uint32_t len_1024 = 0;
// Loop through the block of data
// ------------------------------
for (int pos = 0; pos < noOfSamples; ++pos)
{
m_sample = sampleBuff[pos];
m_valMid += (m_sample - m_valMid) / 1024.0f;
++m_sampleNo;
// Detect Transition
// -----------------
transition = ! ((m_lastSample < m_valMid) ^ (m_sample >= m_valMid)); // use XOR for speed
// If this is a transition
// -----------------------
if (transition)
{
// Calculate samples since last trans
// ----------------------------------
int32_t fractional_1024 = (int32_t)(((m_sample - m_valMid)*1024) / (m_sample - m_lastSample));
if (fractional_1024 < 0) { fractional_1024 = -fractional_1024; }
samplePos_1024 = (m_sampleNo<<10)-fractional_1024;
len_1024 = samplePos_1024 - m_lastTransPos_1024;
m_lastTransPos_1024 = samplePos_1024;
// If symbol is large enough to be valid
// -------------------------------------
if (len_1024 > m_minSymSamples_1024)
{
// Check for shortest good transition
// ----------------------------------
if ((len_1024 < m_shortestGoodTrans_1024) &&
(m_goodTransitions < BAUD_STABLE)) // detect change of symbol size
{
int32_t fractionOfShortest_1024 = (len_1024<<10) / m_shortestGoodTrans_1024;
// If currently at half the baud rate
// ----------------------------------
if ((fractionOfShortest_1024 > 410) && (fractionOfShortest_1024 < 614)) // 0.4 and 0.6
{
m_averageSymbolLen_1024 /= 2;
m_shortestGoodTrans_1024 = len_1024;
}
// If currently at the wrong baud rate
// -----------------------------------
else if (fractionOfShortest_1024 < 768) // 0.75
{
m_averageSymbolLen_1024 = len_1024;
m_shortestGoodTrans_1024 = len_1024;
m_goodTransitions = 0;
m_lastSingleBitPos_1024 = samplePos_1024 - len_1024;
}
}
// Calc the number of bits since events
// ------------------------------------
int32_t halfSymbol_1024 = m_averageSymbolLen_1024 / 2;
int bitsSinceLastTrans = max((uint32_t)1, (len_1024+halfSymbol_1024) / m_averageSymbolLen_1024 );
int bitsSinceLastSingle = (((m_sampleNo<<10)-m_lastSingleBitPos_1024) + halfSymbol_1024) / m_averageSymbolLen_1024;
// Check for single bit
// --------------------
if (bitsSinceLastTrans == 1)
{
m_lastSingleBitPos_1024 = samplePos_1024;
}
// If too long since last transition
// ---------------------------------
if (bitsSinceLastTrans > MAX_CONSEC_SAME)
{
resetVals();
}
// If too long sice last single bit
// --------------------------------
else if (bitsSinceLastSingle > MAX_WITHOUT_SINGLE)
{
resetVals();
}
else
{
// If this is a good transition
// ----------------------------
int32_t offsetFromExtectedTransition_1024 = len_1024 - (bitsSinceLastTrans*m_averageSymbolLen_1024);
if (offsetFromExtectedTransition_1024 < 0) { offsetFromExtectedTransition_1024 = -offsetFromExtectedTransition_1024; }
if (offsetFromExtectedTransition_1024 < ((int32_t)m_averageSymbolLen_1024 / 4)) // Has to be within 1/4 of symbol to be good
{
++m_goodTransitions;
uint32_t bitsCount = min((uint32_t)BAUD_STABLE, m_goodTransitions);
uint32_t propFromPrevious = m_averageSymbolLen_1024*bitsCount;
uint32_t propFromCurrent = (len_1024 / bitsSinceLastTrans);
m_averageSymbolLen_1024 = (propFromPrevious + propFromCurrent) / (bitsCount + 1);
m_badTransitions = 0;
//if ( len < m_shortestGoodTrans ){m_shortestGoodTrans = len;}
// Store the old symbol size
if (m_goodTransitions >= BAUD_STABLE)
{
m_lastStableSymbolLen_1024 = m_averageSymbolLen_1024;
}
}
}
// Set the point of the last bit if not yet stable
// -----------------------------------------------
if ((m_goodTransitions < BAUD_STABLE) || (m_badTransitions > 0))
{
m_lastBitPos_1024 = samplePos_1024 - (m_averageSymbolLen_1024 / 2);
}
// Calculate the exact positiom of the next bit
// --------------------------------------------
int32_t thisPlusHalfsymbol_1024 = samplePos_1024 + (m_averageSymbolLen_1024/2);
int32_t lastPlusSymbol = m_lastBitPos_1024 + m_averageSymbolLen_1024;
m_nextBitPos_1024 = lastPlusSymbol + ((thisPlusHalfsymbol_1024 - lastPlusSymbol) / 16);
// Check for bad pos error
// -----------------------
if (m_nextBitPos_1024 < samplePos_1024) m_nextBitPos_1024 += m_averageSymbolLen_1024;
// Calculate integer sample after next bit
// ---------------------------------------
m_nextBitPosInt = (m_nextBitPos_1024>>10) + 1;
} // symbol is large enough to be valid
else
{
// Bad transition, so reset the counts
// -----------------------------------
++m_badTransitions;
if (m_badTransitions > MAX_BAD_TRANS)
{
resetVals();
}
}
} // end of if transition
// Reached the point of the next bit
// ---------------------------------
if (m_sampleNo >= m_nextBitPosInt)
{
// Everything is good so extract a bit
// -----------------------------------
if (m_goodTransitions > 20)
{
// Store value at the center of bit
// --------------------------------
storeBit();
}
// Check for long 1 or zero
// ------------------------
uint32_t bitsSinceLastTrans = ((m_sampleNo<<10) - m_lastTransPos_1024) / m_averageSymbolLen_1024;
if (bitsSinceLastTrans > m_maxRunOfSameValue)
{
resetVals();
}
// Store the point of the last bit
// -------------------------------
m_lastBitPos_1024 = m_nextBitPos_1024;
// Calculate the exact point of the next bit
// -----------------------------------------
m_nextBitPos_1024 += m_averageSymbolLen_1024;
// Look for the bit after the next bit pos
// ---------------------------------------
m_nextBitPosInt = (m_nextBitPos_1024>>10) + 1;
} // Reached the point of the next bit
m_lastSample = m_sample;
} // Loop through the block of data
return getNoOfBits();
}
// ====================================================================
//
// ====================================================================
void POCSAGProcessor::storeBit()
{
if (++m_bitsStart >= BIT_BUF_SIZE) { m_bitsStart = 0; }
// Calculate the bit value
float sample = (m_sample + m_lastSample) / 2;
//int32_t sample_1024 = m_sample_1024;
bool bit = sample > m_valMid;
// If buffer not full
if (m_bitsStart != m_bitsEnd)
{
// Decide on output val
if (bit)
{
m_bits[m_bitsStart] = 0;
}
else
{
m_bits[m_bitsStart] = 1;
}
}
// Throw away bits if the buffer is full
else
{
if (--m_bitsStart <= -1)
{
m_bitsStart = BIT_BUF_SIZE - 1;
}
}
}
// ====================================================================
//
// ====================================================================
int POCSAGProcessor::extractFrames()
{
int msgCnt = 0;
// While there is unread data in the bits buffer
//----------------------------------------------
while (getNoOfBits() > 0)
{
m_fifo.codeword = (m_fifo.codeword << 1) + getBit();
m_fifo.numBits++;
// If number of bits in fifo equals 32
//------------------------------------
if (m_fifo.numBits >= 32)
{
// Not got sync
// ------------
if (!m_gotSync)
{
if (bitsDiff(m_fifo.codeword, M_SYNC) <= 2)
{
m_inverted = false;
m_gotSync = true;
m_numCode = -1;
m_fifo.numBits = 0;
}
else if (bitsDiff(m_fifo.codeword, M_NOTSYNC) <= 2)
{
m_inverted = true;
m_gotSync = true;
m_numCode = -1;
m_fifo.numBits = 0;
}
else
{
// Cause it to load one more bit
m_fifo.numBits = 31;
}
} // Not got sync
else
{
// Increment the word count
// ------------------------
++m_numCode; // It got set to -1 when a sync was found, now count the 16 words
uint32_t val = m_inverted ? ~m_fifo.codeword : m_fifo.codeword;
OnDataWord(val, m_numCode);
// If at the end of a 16 word block
// --------------------------------
if (m_numCode >= 15)
{
msgCnt += OnDataFrame(m_numCode+1, (m_samplesPerSec<<10) / m_lastStableSymbolLen_1024);
m_gotSync = false;
m_numCode = -1;
}
m_fifo.numBits = 0;
}
} // If number of bits in fifo equals 32
} // While there is unread data in the bits buffer
return msgCnt;
} // extractFrames
// ====================================================================
//
// ====================================================================
short POCSAGProcessor::getBit()
{
if (m_bitsEnd != m_bitsStart)
{
if (++m_bitsEnd >= BIT_BUF_SIZE)
{
m_bitsEnd = 0;
}
return m_bits[m_bitsEnd];
}
else
{
return -1;
}
}
// ====================================================================
//
// ====================================================================
int POCSAGProcessor::getNoOfBits()
{
int bits = m_bitsEnd - m_bitsStart;
if (bits < 0) { bits += BIT_BUF_SIZE; }
return bits;
}
// ====================================================================
//
// ====================================================================
uint32_t POCSAGProcessor::getRate()
{
return ((m_samplesPerSec<<10)+512) / m_lastStableSymbolLen_1024;
}
// ====================================================================
//
// ====================================================================
int main() {
EventDispatcher event_dispatcher { std::make_unique<POCSAGProcessor>() };
event_dispatcher.run();
+148 -30
View File
@@ -40,25 +40,100 @@
#include "portapack_shared_memory.hpp"
#include <cstdint>
#include <bitset>
using namespace std;
// Class used to smooth demodulated waveform prior to decoding
// -----------------------------------------------------------
template <class ValType, class CalcType>
class SmoothVals
{
protected:
ValType * m_lastVals; // Previoius N values
int m_size; // The size N
CalcType m_sumVal; // Running sum of lastVals
int m_pos; // Current position in last vals ring buffer
int m_count; //
class POCSAGProcessor : public BasebandProcessor {
public:
SmoothVals() : m_lastVals(NULL), m_size(1), m_sumVal(0), m_pos(0), m_count(0)
{
m_lastVals = new ValType[m_size];
}
// --------------------------------------------------
// --------------------------------------------------
virtual ~SmoothVals()
{
delete[] m_lastVals;
}
// --------------------------------------------------
// Set size of smoothing
// --------------------------------------------------
void SetSize(int size)
{
m_size = std::max(size, 1);
m_pos = 0;
delete[] m_lastVals;
m_lastVals = new ValType[m_size];
m_sumVal = 0;
}
// --------------------------------------------------
// Get size of smoothing
// --------------------------------------------------
int Size() { return m_size; }
// --------------------------------------------------
// In place processing
// --------------------------------------------------
void Process(ValType * valBuff, int numVals)
{
ValType tmpVal;
if (m_count > (1024*10))
{
// Recalculate the sum value occasionaly, stops accumulated errors when using float
m_count = 0;
m_sumVal = 0;
for (int i = 0; i < m_size; ++i) { m_sumVal += (CalcType)m_lastVals[i]; }
}
// Use a rolling smoothed value while processing the buffer
for (int buffPos = 0; buffPos < numVals; ++buffPos)
{
m_pos = (m_pos + 1); // Increment the position in the stored values
if (m_pos >= m_size) { m_pos = 0; } // loop if reached the end of the stored values
m_sumVal -= (CalcType)m_lastVals[m_pos]; // Subtract the oldest value
m_lastVals[m_pos] = valBuff[buffPos]; // Store the new value
m_sumVal += (CalcType)m_lastVals[m_pos]; // Add on the new value
tmpVal = (ValType)(m_sumVal / m_size); // Scale by number of values smoothed
valBuff[buffPos] = tmpVal;
}
m_count += numVals;
}
};
// --------------------------------------------------
// Class to process base band data to pocsag frames
// --------------------------------------------------
class POCSAGProcessor : public BasebandProcessor{
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
private:
enum rx_states {
WAITING = 0,
PREAMBLE = 32,
SYNC = 64,
//LOSING_SYNC = 65,
//LOST_SYNC = 66,
//ADDRESS = 67,
//MESSAGE = 68,
//END_OF_MESSAGE = 69
};
int OnDataFrame(int len, int baud);
int OnDataWord(uint32_t word, int pos);
private:
static constexpr size_t baseband_fs = 3072000;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
@@ -79,28 +154,71 @@ private:
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
dsp::decimate::FIRAndDecimateComplex channel_filter { };
dsp::demodulate::FM demod { };
SmoothVals<float, float> smooth;
//AudioOutput audio_output { };
AudioOutput audio_output { };
uint32_t sync_timeout { 0 };
uint32_t msg_timeout { 0 };
uint32_t slicer_sr { 0 };
uint32_t sphase { 0 };
uint32_t sphase_delta { 0 };
uint32_t sphase_delta_half { 0 };
uint32_t sphase_delta_eighth { 0 };
uint32_t rx_data { 0 };
uint32_t rx_bit { 0 };
bool configured = false;
rx_states rx_state { WAITING };
pocsag::BitRate bitrate { pocsag::BitRate::FSK1200 };
bool phase;
uint32_t codeword_count { 0 };
pocsag::POCSAGPacket packet { };
void push_packet(pocsag::PacketFlag flag);
void configure(const POCSAGConfigureMessage& message);
void configure();
// ----------------------------------------
// Frame extractraction methods and members
// ----------------------------------------
private:
void initFrameExtraction();
struct FIFOStruct {
unsigned long codeword;
int numBits;
};
#define BIT_BUF_SIZE (64)
void resetVals();
void setFrameExtractParams(long a_samplesPerSec, long a_maxBaud = 8000, long a_minBaud = 200, long maxRunOfSameValue = 32);
int processDemodulatedSamples(float * sampleBuff, int noOfSamples);
int extractFrames();
void storeBit();
short getBit();
int getNoOfBits();
uint32_t getRate();
uint32_t m_averageSymbolLen_1024{0};
uint32_t m_lastStableSymbolLen_1024{0};
uint32_t m_samplesPerSec{0};
uint32_t m_goodTransitions{0};
uint32_t m_badTransitions{0};
uint32_t m_sampleNo{0};
float m_sample{0};
float m_valMid{0.0f};
float m_lastSample{0.0f};
uint32_t m_lastTransPos_1024{0};
uint32_t m_lastSingleBitPos_1024{0};
uint32_t m_nextBitPosInt{0}; // Integer rounded up version to save on ops
uint32_t m_nextBitPos_1024{0};
uint32_t m_lastBitPos_1024{0};
uint32_t m_shortestGoodTrans_1024{0};
uint32_t m_minSymSamples_1024{0};
uint32_t m_maxSymSamples_1024{0};
uint32_t m_maxRunOfSameValue{0};
bitset<(size_t)BIT_BUF_SIZE> m_bits{0};
long m_bitsStart{0};
long m_bitsEnd{0};
FIFOStruct m_fifo{0,0};
bool m_gotSync{false};
int m_numCode{0};
bool m_inverted{false};
};
+4 -3
View File
@@ -29,8 +29,9 @@
#include "utility.hpp"
ReplayProcessor::ReplayProcessor() {
channel_filter_pass_f = taps_200k_decim_1.pass_frequency_normalized * 1000000; // 162760.416666667
channel_filter_stop_f = taps_200k_decim_1.stop_frequency_normalized * 1000000; // 337239.583333333
channel_filter_low_f = taps_200k_decim_1.low_frequency_normalized * 1000000;
channel_filter_high_f = taps_200k_decim_1.high_frequency_normalized * 1000000;
channel_filter_transition = taps_200k_decim_1.transition_normalized * 1000000;
spectrum_samples = 0;
@@ -70,7 +71,7 @@ void ReplayProcessor::execute(const buffer_c8_t& buffer) {
spectrum_samples += buffer.count;
if( spectrum_samples >= spectrum_interval_samples ) {
spectrum_samples -= spectrum_interval_samples;
channel_spectrum.feed(iq_buffer, channel_filter_pass_f, channel_filter_stop_f);
channel_spectrum.feed(iq_buffer, channel_filter_low_f, channel_filter_high_f, channel_filter_transition);
txprogress_message.progress = bytes_read; // Inform UI about progress
txprogress_message.done = false;
+3 -2
View File
@@ -54,8 +54,9 @@ private:
baseband_fs / 8
};
uint32_t channel_filter_pass_f = 0;
uint32_t channel_filter_stop_f = 0;
int32_t channel_filter_low_f = 0;
int32_t channel_filter_high_f = 0;
int32_t channel_filter_transition = 0;
std::unique_ptr<StreamOutput> stream { };

Some files were not shown because too many files have changed in this diff Show More