Compare commits

...

224 Commits

Author SHA1 Message Date
Erwin Ried 3dfb23481f Update README.md 2021-01-20 10:29:13 +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 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 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 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 55783a60f5 Update ui_navigation.cpp 2020-09-15 23:13:31 +02:00
Erwin Ried e76ec66023 Adding contributors 2020-09-15 23:13:01 +02:00
Erwin Ried bf068abba3 Microphone app button rename 2020-09-15 23:12:50 +02:00
Erwin Ried 8949662eee Version bump 2020-09-15 22:10:17 +02:00
Erwin Ried 3d9ce8a037 Merge branch 'pr/166' into Radiosonde-vaisala-add-temp-humidity-merge-conflicts-fix 2020-09-14 23:11:08 +02:00
Erwin Ried 7fd9adf3f2 Merge pull request #171 from euquiq/mic-tx-rx-into-main-menu
Menu changes
2020-09-14 22:51:49 +02:00
Erwin Ried 3aaa4a0ba1 Merge pull request #161 from dqs105/mic_tx_rfgain
Added TX Gain control & code simplification
2020-09-14 22:50:35 +02:00
Erwin Ried 875db13917 Merge pull request #170 from dqs105/siggen_amp
Remove hard-coded rf_amp on.
2020-09-14 22:46:53 +02:00
Erwin Ried 1a94f4a1ab Merge pull request #174 from ITAxReal/ax_fork
SoundBoard App Improvements
2020-09-14 22:46:00 +02:00
Erwin Ried 4390647e28 Merge pull request #184 from rascafr/rascafr/freq-manager-hide-tmp-dot-files
Hide temporary & OS system files from category files list
2020-09-11 22:18:20 +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
Rascafr fcdbedca98 Bad emplace_back code position fix 2020-09-11 20:46:41 +02:00
Rascafr ccb3937e37 Hide .dot tmp / system files from list in frequency manager module 2020-09-11 20:34:15 +02:00
Erwin Ried c4052064a9 Merge pull request #182 from rascafr/file-manager-hide-tmp-dot-files
Hide temporary & OS system in Frequency Manager
2020-09-11 15:02:16 +02:00
Rascafr dd5e63abd3 Hide empty & .dot tmp / system files in freq manager 2020-09-11 12:25:10 +02:00
ITAxReal 8a4e6492d9 More Improvements on SoundBoard
Optimizing and fixing
2020-08-28 23:59:33 +03:00
ITAxReal 668afccc28 > But it's a sound board not a music player....  Maybe make it say sounds instead of songs? Paging was a great idea however.
Okay, okay.
2020-08-28 22:22:39 +03:00
ITAxReal e466c14c88 Improved SoundBoard app:
* No device freeze when you try to close app while it's transmitting
* Bypassed 100 .wav files limit by implementing paging functionality
* Removed useless progressbar and implemented page info line instead
2020-08-28 21:47:56 +03:00
euquiq a2b28c097c Menu changes
Moved the MIC TX RX into the main menu.

Changed scanner color from yellow to green, since it is kind of rounded up into an acceptable functionality.

Also, did a bit of cleanup on the code spacing inside the menues.
2020-08-26 10:48:09 -03:00
dqs105 7fae33eb49 Remove hard-coded rf_amp on.
Removed hard-coded turning rf_amp on when enabling transmitting.
It will be handled by TransmitterView.
2020-08-26 15:39:17 +08:00
Erwin Ried 3425ca8d54 Update generate_world_map.bin.py 2020-08-26 01:40:51 +02:00
Erwin Ried dbf8d2e130 Merge pull request #167 from euquiq/fix-backlight-timer-bug
fix-portapack-backlight-timer-bug
2020-08-25 10:11:31 +02:00
euquiq a445bfa444 fix-portapack-backlight-timer-bug
The selected time for backlight off on Options-> Interface was not working ok for most of the selectable time options.
2020-08-24 18:12:46 -03:00
euquiq 13abb620f6 Radiosonde-vaisala-add-temp-humidity
Also added the fields "DateTime" which just shows the raw timestamp that portapack assigned the last packet received, in the format: YYYYMMDDHHMMSS ... And "Frame" which shows the packet # (or frame) for correlating with other software / verify that there are new packets being received.

Also moved a string function for returning rounded-up decimals, originally inside the whipcalc tool app, into the string_format functions library, because I used that function on TEMP and HUMIDITY values inisde the radiosonde app.

Finally, the whole UI has its widgets moved a bit, giving space for these new parameters.
2020-08-24 17:31:27 -03:00
dqs105 8833b2fce5 Restored radio.cpp 2020-08-24 11:21:30 +08:00
dqs105 94eea961b5 Merge Button and TxButton & Minor bug fix
- TxButton and Button now merged into one widget(with compatibility).
- Fixed Tx stuck when pressing the button with key at the first time.
- Other small bug fix.
2020-08-24 11:18:57 +08:00
dqs105 7d6fb56c7e Minor bug fix
- Key press & touch isolation
2020-08-24 11:09:10 +08:00
dqs105 911eb36210 Merged TxButton and Button & minor bug fix 2020-08-24 11:02:42 +08:00
dqs105 af0d9d8b68 Merge dqs105/mic_tx_rx_dev
Various enhancements

- Separate TX and RX frequency control
- Fixed the TX led bug(What does those code do?)
- UI tweaks
- New PTT button instead of "Right Button" (allowing right button to be always available)
2020-08-24 02:17:36 +08:00
dqs105 a09db01821 Merge dqs105/mic_tx_rx_dev_ui
UI tweak & new PTT button
2020-08-24 01:59:22 +08:00
dqs105 d7568b820d UI tweak & new PTT button 2020-08-24 01:53:34 +08:00
dqs105 0f88efc44e Tx led fix & UI tweak & Rx frequency 2020-08-23 17:41:31 +08:00
dqs105 76f6ab78c2 Merge dqs105/mic_rx_gain
Thanks @euquiq for a more common solution to the bug.

Tweaked UI a little.
Added RX gain control.
Now we have full gain controls!

Merged PTT and Voice activation into one option selector.(allowing RIGHT BUTTON to work with PTT off)
2020-08-22 15:03:53 +08:00
dqs105 4197b5c083 Added RX gain control & UI modification
Thanks @euquiq for a more common solution to the bug.

Added RX gain control.
Now we have full gain controls!

Merged PTT and Voice activation into one option selector.(allowing RIGHT BUTTON to work with PTT off)
2020-08-22 14:57:55 +08:00
Erwin Ried 888225fb55 Merge pull request #162 from euquiq/fix-freq-format-function
Revert into use of spaces for padding freq
2020-08-21 20:48:43 +02:00
euquiq b41074fbe3 Revert into use of spaces for padding freq
This patch addresses the issue detected in: https://github.com/eried/portapack-mayhem/issues/159

This patch will revert the behavior of the function to_string_short_freq

into using spaces on the left of the integer part of the frequency (as it did originally).

When upgrading the scanner app, I did change the behavior of this function eliminating those spaces, so I could gain some characters-worth of space inside the scanner,  but I failed to detect that it introduced some lack of padding on the rx->audio app.

Now, it is back as before, and I also did update the scanner so it can cope with the "extra spaces" this function now adds (again).
2020-08-21 15:05:37 -03:00
dqs105 2f54c11fea Added TX Gain control & code simplification
A bug that enabling audio RX resets the TX gain (perhaps because that changing to receiving mode modifies some registers)  inspired me to add this gain control.
Commented out some steps which don't require for the VU meter to work again.
Moved some widgets for gain control.
2020-08-21 23:18:53 +08:00
Erwin Ried 0852bf89f0 Merge pull request #149 from mcules/v1.2
Update CB.TXT
2020-08-20 21:53:04 +02:00
Erwin Ried f1a2155e87 Merge pull request #157 from eried/gcc9.3-assert-redefinition
Gcc9.3 assert redefinition
2020-08-20 21:45:11 +02:00
Erwin Ried af8382eeb0 Merge pull request #145 from strijar/audio-cw
Audio FIR filter for CW
2020-08-20 20:57:01 +02:00
Erwin Ried 0993f36157 Merge pull request #155 from euquiq/add-crc-check-and-selector-on-radiosondes
Radiosonde-CRC-checkbox
2020-08-20 20:56:21 +02:00
Erwin Ried 629f911b91 Merge pull request #150 from strijar/wide-waterfall
Extended waterfall in AM/NFM Audio mode
2020-08-20 20:54:45 +02:00
euquiq e76a464f7e Radiosonde-CRC-checkbox
Added CRC calculation for Vaisala radiosondes.

Added a Checkbox on APP for turning ON / OFF CRC. When CRC on, malformed packets are ignored.

Connected existing CRC function for METEOMAN sondes, using the same "CRC" checkbox logic.
2020-08-20 15:22:11 -03:00
Erwin Ried 9186031e95 Update stream_output.cpp 2020-08-19 22:52:39 +02:00
eried 1c9ccbc247 Rename m4txevent::assert and m0apptxevent::assert (adding "_event")
Copied from https://github.com/sharebrained/portapack-hackrf/commit/f6cdf6a7229638be5c14ae5879ba4ec0dc0bf29e#
2020-08-19 09:43:04 +02:00
Belousov Oleg 4bc360d652 Waterfall in AM/NFM Audio mode extended to 48kHz 2020-08-19 01:04:14 +03:00
Dennis Eisold 99b8fb9b53 Moved german frequencies
Moved german frequencies
2020-08-18 22:38:46 +02:00
Dennis Eisold a73aaaa433 Delete PMR.TXT
Removed, because was allready there
2020-08-18 22:13:45 +02:00
Dennis Eisold 788bf03104 Update FREENET.TXT
Empty line added
2020-08-18 22:13:17 +02:00
Dennis Eisold 5d21e1d09c Added all CB Bands
Removed CB.TXT and split in own files for each band. Added Bands A-J and alpha
2020-08-18 22:13:04 +02:00
Dennis Eisold b3d34cd3c7 Create PMR.TXT
Added PMR Frequencies
2020-08-18 21:30:54 +02:00
Dennis Eisold 8cb5ddb9ce Create FREENET.TXT
Added Freenet frequencies
2020-08-18 21:26:15 +02:00
Dennis Eisold 33f6edc08a Update CB.TXT
Added channels 41-80 and changed 1-20 to current layout
2020-08-18 20:17:27 +02:00
Белоусов Олег 3559e37a76 Audio FIR filter for CW 2020-08-17 16:07:06 +03:00
Erwin Ried c626d83c3b Merge pull request #139 from eried/v1.2
V1.2
2020-08-15 20:44:16 +02:00
Erwin Ried b7ae9d575b Merge branch 'v1.2' of https://github.com/eried/portapack-mayhem into v1.2 2020-08-15 16:01:33 +02:00
Erwin Ried 303d9c8a9d Version bump 2020-08-15 16:01:15 +02:00
Erwin Ried d79f3ce028 Merge pull request #137 from eried/debug-temperature
Debug temperature
2020-08-15 15:58:36 +02:00
Erwin Ried c56d84d037 Merge pull request #136 from eried/new-scanner-ui
New scanner ui
2020-08-15 15:56:21 +02:00
Erwin Ried 646b7b564e Merge pull request #110 from strijar/audio-spectrum
Audio spectrum
2020-08-15 15:54:08 +02:00
Erwin Ried cc2046b607 Merge pull request #119 from euquiq/MIC_TX_RX_with_volume_and_squelch
MIC TX Now includes RX with Volume and Squelch
2020-08-15 15:53:48 +02:00
Erwin Ried 1fc9a1c6e8 Merge pull request #120 from eried/WAV-viewer-bug-when-wav-file-format-is-wrong
Update ui_view_wav.cpp
2020-08-15 15:53:27 +02:00
Erwin Ried 4b02a1c410 Merge pull request #129 from jwetzell/add-heading-to-geomap
Add heading to geomap
2020-08-15 15:53:05 +02:00
Erwin Ried 5a3da3bd6a Merge branch 'v1.2' into add-heading-to-geomap 2020-08-15 15:52:53 +02:00
Erwin Ried 40785e8094 Merge pull request #135 from euquiq/radiosonde-vaisala-rs41-decoding
Radiosonde-app-Vaisala-rs41-decoding
2020-08-15 15:51:10 +02:00
Erwin Ried f519168602 Merge branch 'new-footer' into v1.2 2020-08-15 15:48:49 +02:00
Erwin Ried ccef06c088 Margin for footer elements 2020-08-15 15:47:23 +02:00
Erwin Ried 8251cf7dbb Hide the seconds at the beginning 2020-08-15 15:46:53 +02:00
Erwin Ried 004fddf4de Skip splash when pressing titlebar, if enabled 2020-08-15 03:20:55 +02:00
euquiq 7b19c0a35f Update ui_navigation.cpp
Now the Sonde App icon is in green: All sondes in the original proposal are working!
2020-08-14 20:11:24 -03:00
jwetzell 27a549dac7 allow printing tag even without a valid heading 2020-08-14 15:46:45 -05:00
euquiq c7b0fbc359 Radiosonde-app-Vaisala-rs41-decoding
Added the Vaisala RS41 data packet decoding.

Changed the default freq from 402.0 to 402.7 Mhz, since it is more popular freq.

Lowered the frequency stepping, so it is easier to fine-tune the exact freq center, if needed.

Sonde's Serial ID is passed into the VIEW MAP, so now the sonde is labelled on the map.
2020-08-14 15:51:12 -03:00
Erwin Ried 6eabd9f1bc Update README.md 2020-08-13 23:17:25 +02:00
Erwin Ried c522d3d8e4 Update README.md 2020-08-13 23:11:36 +02:00
jwetzell f6437adc60 Reorganixe ADSBDetailsView 2020-08-11 11:26:54 -05:00
jwetzell 13ba70f6c5 Fix negative altitude on DetailsView 2020-08-11 11:26:36 -05:00
jwetzell bbae5047d1 Fix ADSB heading math and add heading to ADSB log 2020-08-10 22:55:20 -05:00
Erwin Ried 24d43515cd Update README.md 2020-08-10 14:35:30 +02:00
Erwin Ried f6ad116eb8 Update README.md 2020-08-10 14:33:49 +02:00
Erwin Ried ba6c102a1a Update README.md 2020-08-10 14:17:52 +02:00
Erwin Ried 871be0199a In some scenarios, the new line does not work if is not called thru the write function 2020-08-08 23:08:54 +02:00
Erwin Ried c7082455c1 Super simple about 2020-08-08 23:06:53 +02:00
Erwin Ried fcbb26051b ACARS is not implemented 2020-08-08 15:09:12 +02:00
Erwin Ried 4ce44b84f3 Merge pull request #122 from klockee/new-main-menu-footer
New main menu footer
2020-08-08 11:43:45 +02:00
klockee 90d2dbb49c More cleanup 2020-08-08 04:48:29 -04:00
klockee b300cc258e Cleaned up and tweaked 2020-08-08 04:24:57 -04:00
Erwin Ried b49ad91d24 Update ui_view_wav.cpp 2020-08-07 22:20:54 +02:00
euquiq 6d131ccfd4 sorry, little detail
That include is not needed
2020-08-07 16:35:06 -03:00
euquiq d50ea3cc50 fixed a bug, and some fine-tuning
Earlier code did not start with squelch totally open, but a tiny bit closed.  (now at app loading, squelch is truly set up with the same value it shows on screen).

I also hardcoded the NFM sampling rate and baseband bandwidth. It seemed "the right thing to do".
2020-08-07 16:33:17 -03:00
Erwin Ried 8edf4da511 Update README.md 2020-08-07 12:53:26 +02:00
euquiq 72f3eea131 MIC TX Now includes RX with Volume and Squelch
You can enable RX and adjust VOLUME  and SQUELCH into your liking.

Sadly enough, you will NOT be able to use VOICE ACTIVATION when RX is enabled (to ensure there will be NO audio feedback defeating the VA sensing)

A "bug" that won over me, but perhaps and hopefully other coder can easily fix:  The Vumeter will momentarily "dissappear" when enabling RX. But it will reappear as soon as you start TX. Or when you turn off RX.

I enabled the PEAK LEVEL MARK on the Vumeter, so you can easily see in which level your input voice / signal is peaking and regulate the MIC gain accordingly in an easier / more robust way.

Side enhancement: Took off the dark green, yellow and red coloring from the vumeter when no signal is present, and replaced it with dark_grey. I know that some coloring is "eye-candy" but the vu-meter is more readable with this new contrast.
2020-08-07 00:19:37 -03:00
klockee 9c9021f63b Added new toolbar in main menu 2020-08-05 01:03:51 -04:00
Erwin Ried 62821a79e5 Merge pull request #116 from euquiq/fix-filemanager-rename-placing-file-on-root
fixes file renaming on subdir
2020-08-04 19:45:45 +02:00
euquiq bb264dcf57 fixes file renaming on subdir
The file rename function needs to be called with full_path/old_name  and full_path/new_name.

Instead, it was called with full_path/old_name and new_name ... thus the renamed file ended on the root dir (path not preserved).
2020-08-04 14:24:16 -03:00
Erwin Ried 41cecc88a1 Update README.md 2020-08-04 00:12:51 +02:00
Erwin Ried 17057a5e42 Update README.md 2020-08-03 23:44:30 +02:00
klockee bf2a04ab57 Fix title in splash screen 2020-08-03 05:48:26 -04:00
klockee a982ee029e Merge pull request #1 from eried/master
Update merge
2020-08-03 00:11:38 -04:00
Erwin Ried 00a8d22c5b Merge pull request #105 from euquiq/new-ui-scanner-version
Update ui_scanner.hpp
2020-08-03 00:36:28 +02:00
Erwin Ried e7de39d130 Merge pull request #94 from euquiq/fix-fileman-freeze-no-or-empty-sd
Fixes File manager freezing on absent or empty SD
2020-08-03 00:35:47 +02:00
Erwin Ried 93f752d86f Merge pull request #108 from euquiq/increase-width-fine-tune-cursor-on-spectrum-waterfall
Bigger cursor tick on spectrum waterfall
2020-08-02 17:25:52 +02:00
Erwin Ried 4eb3979ace Merge pull request #112 from klockee/interactive_titlebar
Added interactive titlebar
2020-08-02 17:25:20 +02:00
BuildTools 246e628df8 Added interactive titlebar! 2020-08-01 21:55:09 -04:00
euquiq 1b2c68b3c0 New squelch behavior and fixes
Squelch value now goes from -90 to +20 and it's directly compared against the max_db parameter returned from each freq scanned by the radio subsystem, with no adjusts or manipulation (you adjust the number as will be used).

Less squelch means weaker signals will trigger it. (as expected).

There was a tiny cosmetic bug when you deleted a frequency from the scanning memory: The description was not erased from screen and you could see it while the scan did not resume.

There was another bug on the pause button: If you asked for another manual scan range when paused, the button kept the text "RESUME"  (its text was not reset to "PAUSE" again).
2020-08-01 00:58:34 -03:00
Белоусов Олег 7ec7a9e07c Cleanup 2020-07-31 13:51:35 +03:00
Белоусов Олег 6f58c7929a Spectrum Width and Speed Settings 2020-07-31 13:47:40 +03:00
euquiq ee67f74fa7 Added two new buttons and other enhancements
Added buttons for:

Change scanning direction (ascending / descending)

Saving current freq into the SCANNER.TXT file

(Please notice that, on the other hand -for safety issues- the DEL FQ button, deletes the frequency only from the temp memory on the actual scanning session, but does NOT erases the freq. inside the SCANNER.TXT)

Also there are other bug fixes and scanning speed enhancements.
2020-07-28 21:05:10 -03:00
euquiq 03084251c5 Added MIC TX and FREQ DEL buttons
MIC TX button :Shortcut for jumping into TX -> MIC app.

FREQ DEL button: Deletes currently displayed frequency from temporary scanning memory. Ideal to get rid of those not wanted "noisy" freqs in the middle of a range scan.

Also, some code optimizations thrown in.
2020-07-28 01:21:52 -03:00
euquiq 5ee1f7acb9 Update ui_spectrum.cpp
I changed width in pixels of the "fine-tune cursor" from 2 to 5 , and then re-centered the cursor, from 120 to 118 to accomodate the shift in width.

I was inspired by this old ISSUE on Havoc's repository, where at the end @furrtek commented the need to make the red tick bigger in the future (but forgot / was swamped with other ehnancements / issues):

https://github.com/furrtek/portapack-havoc/issues/172
2020-07-27 01:40:20 -03:00
jwetzell 4c256f65dd Re-add bearing position fix 2020-07-25 12:48:47 -05:00
jwetzell 01101ecef2 position string formatting 2020-07-25 12:26:10 -05:00
euquiq e8f6e1389e better "squelch" and coloring big numbers
When scanner finds a freq with high dbi, it locks into it "listening" a bit more (less than a second) for either confirm or discard it as an actual high dbi or just a spurious thing.

The big number frequency changes color accordingly: Grey = just scanning, yellow = locking in, Green = Found something, allowing the user to listen.
2020-07-25 14:07:03 -03:00
jwetzell 3ec41bc209 Fix angle updating and remove empty callsign 2020-07-25 10:23:46 -05:00
jwetzell f32584c553 Add heading and speed to detail view 2020-07-25 10:22:21 -05:00
jwetzell 542879b74b allow negative heading in maths 2020-07-25 10:21:56 -05:00
jwetzell 62df30eae3 add heading information to adsb details view 2020-07-24 18:56:09 -05:00
jwetzell ea574ef61f Set Sonde heading out of bounds 2020-07-24 16:10:57 -05:00
jwetzell f08949acd7 Add Heading to ADSB and Map Updating 2020-07-24 16:09:21 -05:00
euquiq f50b376cbd Update ui_scanner.hpp
Sorry, I missed this before: These two includes are NOT necessary anymore.
2020-07-20 17:43:30 -03:00
Erwin Ried 4fbdeeab42 Merge pull request #104 from euquiq/new-ui-scanner-version
scanner-enhanced-version
2020-07-20 22:10:43 +02:00
euquiq 27f566be8f scanner-enhanced-version
New ui_scanner, inspired on AlainD's (alain00091) PR: https://github.com/eried/portapack-mayhem/pull/80

It includes the following:

1) A big frequency numbers display.

2) A Manual scan section (you can input a frequency range (START / END), choose a STEP value from an available of standard frequency intervals, and press SCAN button.

3) An AM / WFM / NFM scan mode selector, changing "on the fly".

4) A PAUSE / RESUME button, which will make the scanner to stop upon you listening something of interest

5) AUDIO APP button, a quick shortcut into the analog audio visualizing / recording app, with the mode, frequency, amp, LNA, VGA settings already in tune with the scanner.

6) Two enums are added to freqman.hpp, reserved for compatibility with AlainD's proposed freqman's app and / or further enhancement. More on this topic:

ORIGINAL scanner just used one frequency step, when creating scanning frequency ranges, which was unacceptable.  AlainD enhanced freqman in order to pass different steppings along with ranges.  This seems an excellent idea, and I preserved that aspect on my current implementation of thisscanner, while adding those enums into the freqman just to keep the door open for AlainD's freqman in the future.

7) I did eliminate the extra blank spaces added by function to_string_short_freq() which created unnecessary spacing in every app where there is need for a SHORT string, from a frequency number. (SHORT!, no extra spaces!!)

8) I also maintained AlainD idea of capping the number of frequencies which are dynamically created for each range and stored inside a memory based db. While AlainD capped the number into 400 frequencies, I was able to up that value a bit more, into 500.

Cheers!
2020-07-20 16:43:24 -03:00
Erwin Ried f21e26eaa3 Update problem-upgrading-the-firmware.md 2020-07-15 09:42:11 +02:00
Erwin Ried c13e9ed299 Merge pull request #101 from jwetzell/fix-mictx-gain
Fix MicTX gain when using decimal values
2020-07-15 09:37:43 +02:00
jwetzell 0b12f68a95 Match mic_gain types 2020-07-13 23:45:47 -05:00
euquiq 5b449b182f Addd Newline at the end
tiny detail
2020-07-05 00:39:23 -03:00
euquiq 87cc3c89d5 Fixes File manager freezing on absent or empty SD 2020-07-05 00:33:53 -03:00
eried 645a781ac2 Merge branch 'bearing-fix' 2020-07-02 22:44:16 +02:00
eried 4675ecff59 Update .gitignore 2020-07-02 06:43:54 +02:00
Erwin Ried 6c71574d5d Merge pull request #90 from jwetzell/fix-draw-bearing
Fix draw bearing
2020-07-02 06:40:43 +02:00
jwetzell 2969a0fec9 use screen_rect for center coordinate 2020-07-01 15:39:35 -05:00
jwetzell abb4385859 Adjust polar to point and bearing drawing 2020-07-01 13:16:48 -05:00
Erwin Ried 863bb04091 Merge pull request #89 from euquiq/fixed-datetime-bug
Fixed bug in datetime
2020-06-30 23:44:57 +02:00
euquiq 9a50684c00 Fixed bug in datetime
As shown in https://github.com/eried/portapack-mayhem/issues/88 ...

Tiny bug but probably responsible for badly forming datetime in several apps, as it is used in ACARS, POCSAG and ADSB_TX (and of course AIS RX)
2020-06-30 17:33:38 -03:00
Erwin Ried 0baaeec5f8 Adding badge with latest release to Readme 2020-06-30 10:30:34 +02:00
Erwin Ried 8239c0d300 Update README.md 2020-06-30 10:23:50 +02:00
Erwin Ried bd8e1ea6f9 Update README.md 2020-06-30 10:17:04 +02:00
Erwin Ried 15ee503c39 Update README.md 2020-06-30 10:09:45 +02:00
Erwin Ried a6e5fa4841 Merge pull request #86 from euquiq/better-temperature-graph-on-debug-menu
Nicer debug -> temperature graph
2020-06-29 23:10:16 +02:00
euquiq b0880d6eff Nicer debug -> temperature graph
Lowered the scale -10 ºC so it accomodates less than zero temperatures, present sometimes when cold starting the system.

Added 1 char for temperature label length.

Adjusted the max2837 sensor value -> ºC temp result, by normalizing the conversion to correctly display the standard 25ºC, mentioned in Datasheet.
2020-06-29 18:01:15 -03:00
eried e6c5b3ede5 Version bump 2020-06-28 21:43:22 +02:00
eried 5677b0faf1 Some titles were too long 2020-06-28 21:42:19 +02:00
eried 9096532eee Merge branch 'new-antenna-length-calculator' 2020-06-28 21:35:36 +02:00
eried 728426c5e7 Just minor changes 2020-06-28 21:35:14 +02:00
eried 9ee83c5dbb Update ANTENNAS.TXT 2020-06-28 21:02:43 +02:00
eried ddffc69937 Clear with buffer clear 2020-06-28 20:06:00 +02:00
eried d175e33990 Show infinite+ if freq is 0 and small tweaks 2020-06-28 20:05:31 +02:00
eried dfa524eac0 Slighty larger steps 2020-06-28 17:42:11 +02:00
eried a81ab66994 Show all antennas even if they are off range 2020-06-28 17:41:32 +02:00
eried 3190eec36a Title was too long 2020-06-28 15:41:55 +02:00
Erwin Ried 33a9e1e97c Update README.md 2020-06-28 15:00:42 +02:00
Erwin Ried b640f33710 Update README.md 2020-06-28 14:58:58 +02:00
Erwin Ried a06bf3991c Update README.md 2020-06-28 14:58:38 +02:00
eried 32b07d9f4d Update ANTENNAS.TXT 2020-06-28 14:19:30 +02:00
eried 65ab23fd3c Comma as separator 2020-06-28 14:19:09 +02:00
Erwin Ried a1501d0993 Merge pull request #85 from euquiq/new-improved-antenna-calculator
New Antenna length Calculator
2020-06-28 13:21:46 +02:00
euquiq 8443008dfa New Antenna length Calculator
It reads the antennas definition from a txt file:

WHIPCALC/ANTENNAS.TXT

Inside the textfile you place each antenna you own with the following sintaxis:

<antenna label> <elements length in mm, separated by a space>

For example:

ANT500 185 315 450 586 724 862

Input the required frequency, adjust the wave type (full / half / quarter, etc.) and the calculator will return the antenna length (metric and imperial) while also calculating how much you need to expand the fitting antennas you got defined on the txt.

It may return up to 8 matching antennas, which is more than enough (normally you will have 2, perhaps 3 telescopic antennas around for your portapack)

If by any chance your antennas txt got more than 8 antennas, and more than 8 matches the length of the freq / wave you want, it will only show the first 8 matching antennas and will warn you at the bottom that there are even more results (hidden).

All calculations now are rounded into the best integer, considering first decimal, so precision is double than the original antenna calculator app.
2020-06-27 23:59:11 -03:00
eried 85664c27f8 Merge branch 'update-hackrf-module' 2020-06-18 10:12:28 +02:00
Erwin Ried 1d339af87d Merge pull request #81 from euquiq/fix-gps-sim-exit-while-transmitting-freeze
GPS SIMULATOR EXIT ON PLAY FIX
2020-06-16 09:42:48 +02:00
euquiq 77b56810e8 GPS SIMULATOR EXIT ON PLAY FIX
Now, if you exit the GPS SIMULATOR while transmitting, it will properly stop the radio and won't crash, exiting as expected.
2020-06-16 02:18:29 -03:00
Erwin Ried 704c5de249 Update bug_report.md 2020-06-15 12:37:01 +02:00
eried d6f9d4040d Update hackrf 2020-06-14 22:14:14 +02:00
Erwin Ried 7796e44b4f Merge pull request #78 from euquiq/Whip-calc-rounding-values
Antenna length Calculator fix
2020-06-14 12:40:06 +02:00
euquiq 46c076c296 Antenna length Calculator fix
Calculation now rounds up decimals so result is more accurate.
2020-06-13 21:21:32 -03:00
Erwin Ried 7fba40172f Update README.md 2020-06-14 00:13:55 +02:00
eried 7d5d9054e1 Version bump 2020-06-09 23:44:16 +02:00
eried 62652fdd8a Debug sd card verbose by @gregoryfenton 2020-06-09 23:43:45 +02:00
eried e5992a6be8 Update README.md 2020-06-09 23:11:56 +02:00
Erwin Ried 69ab6d8d3d Update index.html 2020-06-09 00:13:23 +02:00
Erwin Ried e713f48e5f Update README.md 2020-06-08 16:03:48 +02:00
Erwin Ried 73bc0e98d9 Update README.md 2020-06-08 16:02:50 +02:00
Erwin Ried 9f7ef073b6 Merge pull request #66 from gregoryfenton/patch-1
Update README.md
2020-06-08 15:53:12 +02:00
Gregory Fenton ed1225b411 Update README.md 2020-06-08 14:15:04 +01:00
Erwin Ried bb32965ef1 Update README.md 2020-06-08 02:11:01 +02:00
118 changed files with 4450 additions and 1002 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
+3 -3
View File
@@ -20,10 +20,10 @@ Steps to reproduce the behavior:
A clear and concise description of what you expected to happen.
**Affected versions**
Please try to reproduce the problem, and write any difference related with the Expected behavior, on the maximum number of versions:
Please write any difference related with the Expected behavior, on the following versions:
* Latest Stable Release
* Original PortaPack (if possible; this firmware is also included inside the Release package)
* Any previous version (if possible; i.e.: you remember this working on a previous Release)
* Previous one (if any) that did not presented the issue
* Old versions available here: https://github.com/eried/Research/tree/master/HackRF/PortaPack/old_legacy_firmware
**Additional**
If the bug is difficult to explain, additionally to the text please include images and videos.
@@ -8,10 +8,13 @@ assignees: ''
---
**What is happening?**
Describe here why you are unable to upgrade the firmware. Before describing your problems, please read: https://github.com/eried/portapack-havoc/wiki/Update-firmware.
Describe here why you are unable to upgrade the firmware. Before describing your problems, **do the following**:
* Read: https://github.com/eried/portapack-havoc/wiki/Update-firmware
* Watch carefully: https://www.youtube.com/watch?v=_zx4ZvurgOs
* (if you are not in Windows) also check: https://www.youtube.com/watch?v=kjFB58Y1TAo
**Were you able to update the firmware before?**
Things might be confusing the first time, please check the video available on the link above if this is your first time.
Things might be confusing the first time, please check the video available on the link above.
**Can you try the upgrade with a different PC/Portapack/HackRF?**
If is possible, swap hardware and try again. Also, try different USB cables, even if the one you are using works fine for other purposes.
+1
View File
@@ -59,6 +59,7 @@ CMakeFiles/
# Editor files
*.sublime-project
*.sublime-workspace
.vscode
# Host OS leftovers
.DS_Store
+1 -1
View File
@@ -1,3 +1,3 @@
[submodule "hackrf"]
path = hackrf
url = https://github.com/jboone/hackrf.git
url = https://github.com/mossmann/hackrf.git
+40 -23
View File
@@ -1,50 +1,67 @@
> ⚠ **BEFORE BUYING YOURS:** Beware of hardware mods that use a custom firmware, which are [NOT COMPATIBLE](https://github.com/eried/portapack-mayhem/issues/264) with older forks, and they do not follow the license. **ASK THE SELLER if the unit works with an specific firmware, for example, pointing to the [![GitHub release (latest by date)](https://img.shields.io/github/v/release/eried/portapack-mayhem?label=latest%20release&style=social)](https://github.com/eried/portapack-mayhem/releases/latest) on this repository**
# 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) [![Docker Hub Pulls](https://img.shields.io/docker/pulls/eried/portapack.svg)](https://hub.docker.com/r/eried/portapack)
[![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)
This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/), which 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 means it is a derivated, so it is basically a clone that has extra features and fixes over the older versions.
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.
[<img src="https://raw.githubusercontent.com/wiki/eried/portapack-mayhem/img/hw_overview_h2_front.png" height="400">](https://github.com/eried/portapack-mayhem/wiki/Hardware-overview) [<img src="https://raw.githubusercontent.com/wiki/eried/portapack-mayhem/img/hw_overview_h2_inside.png" height="400">](https://github.com/eried/portapack-mayhem/wiki/Hardware-overview#portapack-internals)
*[PortaPack H2](https://s.click.aliexpress.com/e/_dSMPvNo) (clone) with a custom [3d printed case](https://github.com/eried/portapack-mayhem/wiki/H2-Enclosure)*
*[PortaPack H2+HackRF+battery](https://s.click.aliexpress.com/e/_dZ7lA96) (clone) with a custom [3d printed case](https://github.com/eried/portapack-mayhem/wiki/H2-Enclosure)*
## FAQ
# Quick overview
The intention of the current repository is to try to get all the latest features and cleanest documentation for them, embracing any colaboration. To clarify any basic question, please read the following common ones:
If you are new to *HackRF+PortaPack+Mayhem*, there is an awesome introductory video by [Tech Minds](https://www.youtube.com/channel/UC9a8Z6Sp6eb2s3O79pX5Zvg) available:
### Does it work on H1/H2 PortaPack?
[![Setup and overview](https://img.youtube.com/vi/kjFB58Y1TAo/0.jpg)](https://www.youtube.com/watch?v=kjFB58Y1TAo)
Yes, those are the [same](https://github.com/eried/portapack-mayhem/wiki/First-steps). The one I am using to test all changes is this [Portapack H2+HackRF+battery](https://s.click.aliexpress.com/e/_dSMPvNo), which is a clone that includes everything you need. Sadly, the people making the H2 never published the updated PCB, so it is not "optimal" for the community.
# Frequently Asked Questions
### Where is the latest firmware?
This repository expands upon the previous work by many people and aims to constantly add new features, bugfixes and generate documentation to make further development easier. [Collaboration](https://github.com/eried/portapack-mayhem/wiki/How-to-collaborate) is always welcomed and appreciated.
The current stable release is on the [Release](https://github.com/eried/portapack-mayhem/releases/latest) page. Follow the instructions you can find in the release description.
## Does it work on H1/H2 PortaPack?
There is also [nightly builds](https://github.com/eried/portapack-mayhem/releases/tag/nightly) generated frequently, but these may contain incomplete functionality.
Yes, both devices are the [same](https://github.com/eried/portapack-mayhem/wiki/First-steps). The one I am using to test all changes is this [PortaPack H2+HackRF+battery](https://s.click.aliexpress.com/e/_dZ7lA96), which is a kit that includes everything you need. Sadly, the people making the H2 never made the updated schematics available, which is not ideal (and goes against the terms of the license). Most members of the community are using a clone of the [PortaPack H1+HackRF+metal case](https://s.click.aliexpress.com/e/_dS6liw4), which does not include any battery functionality, but it is a cheaper alternative.
### Is this the newest firmware for my PortaPack?
Probably **YES**.
To support the people behind the hardware, please buy a genuine [HackRF](https://greatscottgadgets.com/hackrf/) and [PortaPack](https://store.sharebrained.com/products/portapack-for-hackrf-one-kit).
### Which one is actually the newest?
## 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.
## 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*.
## Which one is actually the newest?
There is a lot of confusion of which is the latest version because no old version used any actual "version number". Additionally, since the files were distributed on facebook groups, github issue links and similar temporal sources, then there was no central location for them.
This fork (**Mayhem**) uses *major.minor.release* [semantic versioning](https://en.wikipedia.org/wiki/Software_versioning), so you can always compare your current version with the latest from [Releases](https://github.com/eried/portapack-mayhem/releases/latest).
### What about Havoc/GridRF/jamesshao8/jboone's?
* jboone's PortaPack: the [vainilla](https://en.wikipedia.org/wiki/Vanilla_software) experience
* Havoc: It was the most popular fork of jboone's PortaPack, right now is readonly so it is not being developed
* jamesshao8: He keeps his own version of the fork, while not attached as a fork to anything
## 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. 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 colaborate
You can write [documentation](https://github.com/eried/portapack-mayhem/wiki), fix bugs and [answer issues](https://github.com/eried/portapack-mayhem/issues) or add new functionality. Please check the following [guide](https://github.com/eried/portapack-mayhem/wiki/How-to-colaborate) with details.
## How can I collaborate
You can write [documentation](https://github.com/eried/portapack-mayhem/wiki), fix bugs and [answer issues](https://github.com/eried/portapack-mayhem/issues) or add new functionality. Please check the following [guide](https://github.com/eried/portapack-mayhem/wiki/How-to-collaborate) with details.
Consider that the hardware and firmware has been created and maintain by a [lot](https://github.com/mossmann/hackrf/graphs/contributors) of [people](https://github.com/eried/portapack-mayhem/graphs/contributors), so always try colaborating your time and effort first.
Consider that the hardware and firmware has been created and maintain by a [lot](https://github.com/mossmann/hackrf/graphs/contributors) of [people](https://github.com/eried/portapack-mayhem/graphs/contributors), so always try colaborating your time and effort first. For coding related questions, if something does not fit as an issue, please join our Discord by clicking the chat badge on [top](#portapack-mayhem).
As a last option, if you want to send money directly to me for getting more boards, antennas and such:
As a last option, if you want to send money directly to me for getting more boards, antennas and such, please use:
[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=CBPQA4HRRPJQ6&source=url)
### What if I need help?
## What if I really want to pay for something?
You can create a bounty and invite people to your own bounty. This will incentivize coders to work on a new feature, solving a bug or even writting documentation. Start a bounty by [creating](https://github.com/eried/portapack-mayhem/issues/new/choose) or [choosing](https://github.com/eried/portapack-mayhem/issues/) an existing issue. Then, go to [Bountysource](https://www.bountysource.com/) and post a bounty using the link to that specific [issue](https://www.bountysource.com/teams/portapack-mayhem/issues).
Promote your bounty over our Discord by clicking the chat badge on [top](#portapack-mayhem).
## What if I need help?
First, check the [documentation](https://github.com/eried/portapack-mayhem/wiki). If you find a bug or you think the problem is related to the current repository, please open an [issue](https://github.com/eried/portapack-mayhem/issues/new/choose).
You can reach the [official community](https://www.facebook.com/groups/177623356165819) in Facebook.
You can reach the [official community](https://www.facebook.com/groups/177623356165819) in Facebook, and our Discord by clicking the chat badge on [top](#portapack-mayhem).
## What if I find incongruencies, or grammatical errors in the text?
If is on the [Wiki](https://github.com/eried/portapack-mayhem/wiki), you can modify it directly. If is on files of the repository, you can send corrections as [pull requests](https://github.com/eried/portapack-mayhem/wiki/How-to-collaborate#coding-new-stuff-or-fixing-bugs). As a last resource, open an [issue](https://github.com/eried/portapack-mayhem/issues/new/choose).
+2 -2
View File
@@ -1,6 +1,6 @@
<head>
<meta http-equiv="refresh" content="0; URL=https://github.com/eried/portapack-havoc/releases" />
<meta http-equiv="refresh" content="0; URL=https://github.com/eried/portapack-mayhem/" />
</head>
<body>
<p>If you are not redirected, <a href="https://github.com/eried/portapack-havoc/releases">click here</a>.</p>
<p>If you are not redirected, <a href="https://github.com/eried/portapack-mayhem/">click here</a>.</p>
</body>
+2 -1
View File
@@ -214,7 +214,7 @@ set(CPPSRC
ui/ui_tabview.cpp
ui/ui_textentry.cpp
ui/ui_transmitter.cpp
apps/ui_about.cpp
apps/ui_about_simple.cpp
apps/ui_adsb_rx.cpp
apps/ui_adsb_tx.cpp
apps/ui_afsk_rx.cpp
@@ -231,6 +231,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
+1 -1
View File
@@ -240,7 +240,7 @@ AISRecentEntryDetailView::AISRecentEntryDetailView(NavigationView& nav) {
void AISRecentEntryDetailView::update_position() {
if (send_updates)
geomap_view->update_position(ais::format::latlon_float(entry_.last_position.latitude.normalized()), ais::format::latlon_float(entry_.last_position.longitude.normalized()));
geomap_view->update_position(ais::format::latlon_float(entry_.last_position.latitude.normalized()), ais::format::latlon_float(entry_.last_position.longitude.normalized()), (float)entry_.last_position.true_heading);
}
void AISRecentEntryDetailView::focus() {
+56 -4
View File
@@ -83,6 +83,32 @@ NBFMOptionsView::NBFMOptionsView(
};
}
/* SPECOptionsView *******************************************************/
SPECOptionsView::SPECOptionsView(
AnalogAudioView* view, const Rect parent_rect, const Style* const style
) : View { parent_rect }
{
set_style(style);
add_children({
&label_config,
&options_config,
&text_speed,
&field_speed
});
options_config.set_selected_index(view->get_spec_bw_index());
options_config.on_change = [this, view](size_t n, OptionsField::value_t bw) {
view->set_spec_bw(n, bw);
};
field_speed.set_value(view->get_spec_trigger());
field_speed.on_change = [this, view](int32_t v) {
view->set_spec_trigger(v);
};
}
/* AnalogAudioView *******************************************************/
AnalogAudioView::AnalogAudioView(
@@ -157,6 +183,29 @@ AnalogAudioView::AnalogAudioView(
on_modulation_changed(static_cast<ReceiverModel::Mode>(modulation));
}
size_t AnalogAudioView::get_spec_bw_index() {
return spec_bw_index;
}
void AnalogAudioView::set_spec_bw(size_t index, uint32_t bw) {
spec_bw_index = index;
spec_bw = bw;
baseband::set_spectrum(bw, spec_trigger);
receiver_model.set_sampling_rate(bw);
receiver_model.set_baseband_bandwidth(bw/2);
}
uint16_t AnalogAudioView::get_spec_trigger() {
return spec_trigger;
}
void AnalogAudioView::set_spec_trigger(uint16_t trigger) {
spec_trigger = trigger;
baseband::set_spectrum(spec_bw, spec_trigger);
}
AnalogAudioView::~AnalogAudioView() {
// TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do
// both?
@@ -272,6 +321,7 @@ void AnalogAudioView::on_show_options_modulation() {
break;
case ReceiverModel::Mode::SpectrumAnalysis:
widget = std::make_unique<SPECOptionsView>(this, nbfm_view_rect, &style_options_group);
waterfall.show_audio_spectrum_view(false);
text_ctcss.hidden(true);
break;
@@ -315,15 +365,17 @@ void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) {
}
baseband::run_image(image_tag);
if (modulation == ReceiverModel::Mode::SpectrumAnalysis) {
baseband::set_spectrum(20000000, 127);
baseband::set_spectrum(spec_bw, spec_trigger);
}
const auto is_wideband_spectrum_mode = (modulation == ReceiverModel::Mode::SpectrumAnalysis);
receiver_model.set_modulation(modulation);
receiver_model.set_sampling_rate(is_wideband_spectrum_mode ? 20000000 : 3072000);
receiver_model.set_baseband_bandwidth(is_wideband_spectrum_mode ? 12000000 : 1750000);
receiver_model.set_sampling_rate(is_wideband_spectrum_mode ? spec_bw : 3072000);
receiver_model.set_baseband_bandwidth(is_wideband_spectrum_mode ? spec_bw/2 : 1750000);
receiver_model.enable();
// TODO: This doesn't belong here! There's a better way.
+49 -1
View File
@@ -58,6 +58,7 @@ private:
{ "DSB ", 0 },
{ "USB ", 0 },
{ "LSB ", 0 },
{ "CW ", 0 },
}
};
};
@@ -94,6 +95,43 @@ private:
};
};
class AnalogAudioView;
class SPECOptionsView : public View {
public:
SPECOptionsView(AnalogAudioView* view, const Rect parent_rect, const Style* const style);
private:
Text label_config {
{ 0 * 8, 0 * 16, 2 * 8, 1 * 16 },
"BW",
};
OptionsField options_config {
{ 3 * 8, 0 * 16 },
4,
{
{ "20m ", 20000000 },
{ "10m ", 10000000 },
{ " 5m ", 5000000 },
{ " 2m ", 2000000 },
{ " 1m ", 1000000 },
{ "500k", 500000 },
}
};
Text text_speed {
{ 9 * 8, 0 * 16, 8 * 8, 1 * 16 },
"SP /63"
};
NumberField field_speed {
{ 12 * 8, 0 * 16 },
2,
{ 0, 63 },
1,
' ',
};
};
class AnalogAudioView : public View {
public:
AnalogAudioView(NavigationView& nav);
@@ -106,13 +144,23 @@ public:
void focus() override;
std::string title() const override { return "Analog audio"; };
size_t get_spec_bw_index();
void set_spec_bw(size_t index, uint32_t bw);
uint16_t get_spec_trigger();
void set_spec_trigger(uint16_t trigger);
private:
static constexpr ui::Dim header_height = 3 * 16;
const Rect options_view_rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 };
const Rect nbfm_view_rect { 0 * 8, 1 * 16, 18 * 8, 1 * 16 };
size_t spec_bw_index = 0;
uint32_t spec_bw = 20000000;
uint16_t spec_trigger = 63;
NavigationView& nav_;
//bool exit_on_squelch { false };
@@ -234,6 +234,8 @@ GpsSimAppView::~GpsSimAppView() {
void GpsSimAppView::on_hide() {
// TODO: Terrible kludge because widget system doesn't notify Waterfall that
// it's being shown or hidden.
if( is_active() )
stop(false);
waterfall.on_hide();
View::on_hide();
}
+45 -12
View File
@@ -48,7 +48,7 @@ void SoundBoardView::stop() {
void SoundBoardView::handle_replay_thread_done(const uint32_t return_code) {
stop();
progressbar.set_value(0);
//progressbar.set_value(0);
if (return_code == ReplayThread::END_OF_FILE) {
if (check_random.value()) {
@@ -90,7 +90,7 @@ void SoundBoardView::start_tx(const uint32_t id) {
playing_id = id;
progressbar.set_max(reader->sample_count());
//progressbar.set_max(reader->sample_count());
//button_play.set_bitmap(&bitmap_stop);
@@ -130,7 +130,7 @@ void SoundBoardView::start_tx(const uint32_t id) {
}*/
void SoundBoardView::on_tx_progress(const uint32_t progress) {
progressbar.set_value(progress);
//progressbar.set_value(progress);
}
void SoundBoardView::on_select_entry() {
@@ -141,8 +141,10 @@ void SoundBoardView::refresh_list() {
auto reader = std::make_unique<WAVFileReader>();
file_list.clear();
c_page = page;
// List directories and files, put directories up top
uint32_t count = 0;
for (const auto& entry : std::filesystem::directory_iterator(u"WAV", u"*")) {
if (std::filesystem::is_regular_file(entry.status())) {
if (entry.path().string().length()) {
@@ -158,21 +160,32 @@ void SoundBoardView::refresh_list() {
if ((reader->channels() == 1) && (reader->bits_per_sample() == 8)) {
//sounds[c].ms_duration = reader->ms_duration();
//sounds[c].path = u"WAV/" + entry.path().native();
file_list.push_back(entry.path());
if (file_list.size() == 100)
break;
if (count >= (page - 1) * 100 && count < page * 100){
file_list.push_back(entry.path());
if (file_list.size() == 100){
page++;
break;
}
}
count++;
}
}
}
}
}
}
if (!file_list.size()) {
// Hide widgets, show warning
menu_view.hidden(true);
text_empty.hidden(false);
set_dirty();
if (page == 1){
menu_view.hidden(true);
text_empty.hidden(false);
set_dirty();
}else{
page = 1;
refresh_list();
return;
}
} else {
// Hide warning, show widgets
menu_view.hidden(false);
@@ -191,9 +204,14 @@ void SoundBoardView::refresh_list() {
}
});
}
page_info.set("Page: " + to_string_dec_uint(c_page) + " Sounds: " + to_string_dec_uint(file_list.size()));
menu_view.set_highlighted(0); // Refresh
}
if (file_list.size() < 100){
page = 1;
}
}
SoundBoardView::SoundBoardView(
@@ -209,13 +227,27 @@ SoundBoardView::SoundBoardView(
&options_tone_key,
//&text_title,
//&text_duration,
&progressbar,
//&progressbar,
&page_info,
&check_loop,
&check_random,
&button_prev_page,
&button_next_page,
&tx_view
});
refresh_list();
button_next_page.on_select = [this](Button&) {
this->refresh_list();
};
button_prev_page.on_select = [this](Button&) {
if (c_page == 1) return;
if (c_page == 2) page = 1;
page = c_page - 1;
refresh_list();
};
//text_title.set(to_string_dec_uint(file_list.size()));
@@ -243,6 +275,7 @@ SoundBoardView::SoundBoardView(
}
SoundBoardView::~SoundBoardView() {
stop();
transmitter_model.disable();
baseband::shutdown();
}
+21 -5
View File
@@ -58,6 +58,8 @@ private:
tx_modes tx_mode = NORMAL;
uint32_t playing_id { };
uint32_t page = 1;
uint32_t c_page = 1;
std::vector<std::filesystem::path> file_list { };
@@ -83,7 +85,21 @@ private:
//{ { 0, 20 * 8 + 4 }, "Title:", Color::light_grey() },
{ { 0, 180 }, "Key:", Color::light_grey() }
};
Button button_next_page {
{ 30 * 7, 25 * 8, 10 * 3, 2 * 14 },
"=>"
};
Button button_prev_page {
{ 17 * 10, 25 * 8, 10 * 3, 2 * 14 },
"<="
};
Text page_info {
{ 0, 30 * 8 - 4, 30 * 8, 16 }
};
MenuView menu_view {
{ 0, 0, 240, 175 },
true
@@ -114,14 +130,14 @@ private:
};
Checkbox check_random {
{ 10 * 8, 25 * 8 + 4 },
{ 10 * 7, 25 * 8 + 4 },
6,
"Random"
};
ProgressBar progressbar {
{ 0 * 8, 30 * 8 - 4, 30 * 8, 16 }
};
//ProgressBar progressbar {
// { 0 * 8, 30 * 8 - 4, 30 * 8, 16 }
//};
TransmitterView tx_view {
16 * 16,
@@ -0,0 +1,77 @@
#include "ui_about_simple.hpp"
namespace ui
{
AboutView::AboutView(NavigationView &nav)
{
add_children({&console, &button_ok});
button_ok.on_select = [&nav](Button &) {
nav.pop();
};
console.writeln("\x1B\x07List of contributors:\x1B\x10");
console.writeln("");
}
void AboutView::update()
{
if (++timer > 200)
{
timer = 0;
switch (++frame)
{
case 1:
// TODO: Generate this automatically from github
// https://github.com/eried/portapack-mayhem/graphs/contributors?to=2022-01-01&from=2020-04-12&type=c
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("");
break;
case 2:
// https://github.com/eried/portapack-mayhem/graphs/contributors?to=2020-04-12&from=2015-07-31&type=c
console.writeln("\x1B\x06Havoc:\x1B\x10");
console.writeln("furrtek,mrmookie,notpike");
console.writeln("mjwaxios,ImDroided,Giorgiofox");
console.writeln("F4GEV,z4ziggy,xmycroftx");
console.writeln("troussos,silascutler");
console.writeln("nickbouwhuis,msoose,leres");
console.writeln("joakar,dhoetger,clem-42");
console.writeln("brianlechthaler,ZeroChaos-...");
console.writeln("");
break;
case 3:
// https://github.com/eried/portapack-mayhem/graphs/contributors?from=2014-07-05&to=2015-07-31&type=c
console.writeln("\x1B\x06PortaPack:\x1B\x10");
console.writeln("jboone,argilo");
console.writeln("");
break;
case 4:
// https://github.com/mossmann/hackrf/graphs/contributors
console.writeln("\x1B\x06HackRF:\x1B\x10");
console.writeln("mossmann,dominicgs,bvernoux");
console.writeln("bgamari,schneider42,miek");
console.writeln("willcode,hessu,Sec42");
console.writeln("yhetti,ckuethe,smunaut");
console.writeln("wishi,mrbubble62,scateu...");
console.writeln("");
frame = 0; // Loop
break;
}
}
}
void AboutView::focus()
{
button_ok.focus();
}
} /* namespace ui */
@@ -0,0 +1,40 @@
#ifndef __UI_ABOUT_SIMPLE_H__
#define __UI_ABOUT_SIMPLE_H__
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_font_fixed_8x16.hpp"
#include <cstdint>
namespace ui
{
class AboutView : public View
{
public:
AboutView(NavigationView &nav);
void focus() override;
std::string title() const override { return "About"; };
int32_t timer{180};
short frame{0};
private:
void update();
Console console{
{0, 10, 240, 240}};
Button button_ok{
{240/3, 270, 240/3, 24},
"OK",
};
MessageHandlerRegistration message_handler_update{
Message::ID::DisplayFrameSync,
[this](const Message *const) {
this->update();
}};
};
} // namespace ui
#endif /*__UI_ABOUT_SIMPLE_H__*/
+20 -7
View File
@@ -92,12 +92,16 @@ void ADSBRxDetailsView::update(const AircraftRecentEntry& entry) {
text_last_seen.set(to_string_dec_uint(age / 60) + " minutes ago");
text_infos.set(entry_copy.info_string);
if(entry_copy.velo.heading < 360 && entry_copy.velo.speed >=0){ //I don't like this but...
text_info2.set("Hdg:" + to_string_dec_uint(entry_copy.velo.heading) + " Spd:" + to_string_dec_int(entry_copy.velo.speed));
}else{
text_info2.set("");
}
text_frame_pos_even.set(to_string_hex_array(entry_copy.frame_pos_even.get_raw_data(), 14));
text_frame_pos_odd.set(to_string_hex_array(entry_copy.frame_pos_odd.get_raw_data(), 14));
if (send_updates)
geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude);
geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, entry_copy.velo.heading);
}
ADSBRxDetailsView::~ADSBRxDetailsView() {
@@ -123,6 +127,7 @@ ADSBRxDetailsView::ADSBRxDetailsView(
&text_airline,
&text_country,
&text_infos,
&text_info2,
&text_frame_pos_even,
&text_frame_pos_odd,
&button_see_map
@@ -172,7 +177,7 @@ ADSBRxDetailsView::ADSBRxDetailsView(
GeoPos::alt_unit::FEET,
entry_copy.pos.latitude,
entry_copy.pos.longitude,
0,
entry_copy.velo.heading,
[this]() {
send_updates = false;
});
@@ -199,7 +204,7 @@ 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()) {
rtcGetTime(&RTCD1, &datetime);
auto& entry = ::on_packet(recent, ICAO_address);
@@ -214,6 +219,7 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
if (frame.get_DF() == DF_ADSB) {
uint8_t msg_type = frame.get_msg_type();
uint8_t msg_sub = frame.get_msg_sub();
uint8_t * raw_data = frame.get_raw_data();
if ((msg_type >= 1) && (msg_type <= 4)) {
@@ -224,10 +230,10 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
entry.set_frame_pos(frame, raw_data[6] & 4);
if (entry.pos.valid) {
str_info = "Alt:" + to_string_dec_uint(entry.pos.altitude) +
" Lat" + to_string_dec_int(entry.pos.latitude) +
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) +
" 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);
@@ -236,6 +242,13 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
if (send_updates)
details_view->update(entry);
}
} else if(msg_type == 19 && msg_sub >= 1 && msg_sub <= 4){
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();
+17 -7
View File
@@ -49,7 +49,7 @@ struct AircraftRecentEntry {
uint16_t hits { 0 };
uint32_t age { 0 };
adsb_pos pos { false, 0, 0, 0 };
adsb_vel velo { false, 0, 999 };
ADSBFrame frame_pos_even { };
ADSBFrame frame_pos_odd { };
@@ -86,6 +86,10 @@ struct AircraftRecentEntry {
pos = decode_frame_pos(frame_pos_even, frame_pos_odd);
}
}
void set_frame_velo(ADSBFrame& frame){
velo = decode_frame_velo(frame);
}
void set_info_string(std::string& new_info_string) {
info_string = new_info_string;
@@ -132,7 +136,7 @@ public:
void update(const AircraftRecentEntry& entry);
std::string title() const override { return "Aircraft details"; };
std::string title() const override { return "Details"; };
private:
AircraftRecentEntry entry_copy { 0 };
@@ -146,8 +150,8 @@ private:
{ { 0 * 8, 2 * 16 }, "Last seen:", Color::light_grey() },
{ { 0 * 8, 3 * 16 }, "Airline:", Color::light_grey() },
{ { 0 * 8, 5 * 16 }, "Country:", Color::light_grey() },
{ { 0 * 8, 12 * 16 }, "Even position frame:", Color::light_grey() },
{ { 0 * 8, 14 * 16 }, "Odd position frame:", Color::light_grey() }
{ { 0 * 8, 13 * 16 }, "Even position frame:", Color::light_grey() },
{ { 0 * 8, 15 * 16 }, "Odd position frame:", Color::light_grey() }
};
Text text_callsign {
@@ -174,17 +178,23 @@ private:
{ 0 * 8, 6 * 16, 30 * 8, 16 },
"-"
};
Text text_info2 {
{0*8, 7*16, 30*8, 16},
"-"
};
Text text_frame_pos_even {
{ 0 * 8, 13 * 16, 30 * 8, 16 },
{ 0 * 8, 14 * 16, 30 * 8, 16 },
"-"
};
Text text_frame_pos_odd {
{ 0 * 8, 15 * 16, 30 * 8, 16 },
{ 0 * 8, 16 * 16, 30 * 8, 16 },
"-"
};
Button button_see_map {
{ 8 * 8, 8 * 16, 14 * 8, 3 * 16 },
{ 8 * 8, 9 * 16, 14 * 8, 3 * 16 },
"See on map"
};
};
+1
View File
@@ -82,6 +82,7 @@ private:
6,
SymField::SYMFIELD_ALPHANUM
};
NumberField num_ssid_dest {
{ 19 * 8, 2 * 16 },
2,
+1 -1
View File
@@ -39,7 +39,7 @@ public:
void focus() override;
std::string title() const override { return "Si44xx transmit"; };
std::string title() const override { return "Si44xx TX"; };
private:
enum tx_modes {
+1 -1
View File
@@ -121,7 +121,7 @@ void TemperatureWidget::paint(Painter& painter) {
}
TemperatureWidget::temperature_t TemperatureWidget::temperature(const sample_t sensor_value) const {
return -45 + sensor_value * 5;
return -35 + sensor_value * 4; //max2837 datasheet temp 25ºC has sensor value: 15
}
std::string TemperatureWidget::temperature_str(const temperature_t temperature) const {
+2 -2
View File
@@ -101,10 +101,10 @@ private:
std::string temperature_str(const temperature_t temperature) const;
static constexpr temperature_t display_temp_min = 0;
static constexpr temperature_t display_temp_min = -10; //Accomodate negative values, present in cold startup cases
static constexpr temperature_t display_temp_scale = 3;
static constexpr int bar_width = 1;
static constexpr int temp_len = 3;
static constexpr int temp_len = 4; //Now scale shows up to 4 chars ("-10C")
};
class TemperatureView : public View {
+81 -68
View File
@@ -41,10 +41,12 @@ void FileManBaseView::load_directory_contents(const std::filesystem::path& dir_p
// List directories and files, put directories up top
if (dir_path.string().length())
entry_list.push_back({ u"..", 0, true });
for (const auto& entry : std::filesystem::directory_iterator(dir_path, u"*")) {
if (std::filesystem::is_regular_file(entry.status())) {
if (entry.path().string().length()) {
// do not display dir / files starting with '.' (hidden / tmp)
if (entry.path().string().length() && entry.path().filename().string()[0] != '.') {
if (std::filesystem::is_regular_file(entry.status())) {
bool matched = true;
if (filtering) {
auto entry_extension = entry.path().extension().string();
@@ -58,9 +60,9 @@ void FileManBaseView::load_directory_contents(const std::filesystem::path& dir_p
if (matched)
entry_list.push_back({ entry.path(), (uint32_t)entry.size(), false });
} else if (std::filesystem::is_directory(entry.status())) {
entry_list.insert(entry_list.begin(), { entry.path(), 0, true });
}
} else if (std::filesystem::is_directory(entry.status())) {
entry_list.insert(entry_list.begin(), { entry.path(), 0, true });
}
}
}
@@ -92,31 +94,37 @@ FileManBaseView::FileManBaseView(
) : nav_ (nav),
extension_filter { filter }
{
load_directory_contents(current_path);
if (!entry_list.size())
empty_root = true;
add_children({
&labels,
&text_current,
&button_exit
});
menu_view.on_left = [&nav, this]() {
load_directory_contents(get_parent_dir());
refresh_list();
};
button_exit.on_select = [this, &nav](Button&) {
nav.pop();
};
};
if (!sdcIsCardInserted(&SDCD1)) {
empty_root=true;
text_current.set("NO SD CARD!");
} else {
load_directory_contents(current_path);
if (!entry_list.size())
{
empty_root = true;
text_current.set("EMPTY SD CARD!");
} else {
menu_view.on_left = [&nav, this]() {
load_directory_contents(get_parent_dir());
refresh_list();
};
}
}
}
void FileManBaseView::focus() {
if (empty_root) {
button_exit.focus();
nav_.display_modal("Error", "No files in root.", ABORT, nullptr);
} else {
menu_view.focus();
}
@@ -190,7 +198,6 @@ void FileManBaseView::refresh_list() {
nav_.pop();
});
}
FileSaveView::FileSaveView(
NavigationView& nav
) : FileManBaseView(nav)
@@ -244,7 +251,11 @@ FileLoadView::FileLoadView(
void FileManagerView::on_rename(NavigationView& nav) {
text_prompt(nav, name_buffer, max_filename_length, [this](std::string& buffer) {
rename_file(get_selected_path(), buffer);
std::string destination_path = current_path.string();
if (destination_path.back() != '/')
destination_path += '/';
destination_path = destination_path + buffer;
rename_file(get_selected_path(), destination_path);
load_directory_contents(current_path);
refresh_list();
});
@@ -271,57 +282,59 @@ FileManagerView::FileManagerView(
NavigationView& nav
) : FileManBaseView(nav, "")
{
on_refresh_widgets = [this](bool v) {
refresh_widgets(v);
};
add_children({
&menu_view,
&labels,
&text_date,
&button_rename,
&button_new_dir,
&button_delete
});
menu_view.on_highlight = [this]() {
text_date.set(to_string_FAT_timestamp(file_created_date(get_selected_path())));
};
refresh_list();
on_select_entry = [this]() {
if (entry_list[menu_view.highlighted_index()].is_directory) {
load_directory_contents(get_selected_path());
refresh_list();
} else
button_rename.focus();
};
button_new_dir.on_select = [this, &nav](Button&) {
name_buffer.clear();
if (!empty_root) {
on_refresh_widgets = [this](bool v) {
refresh_widgets(v);
};
text_prompt(nav, name_buffer, max_filename_length, [this](std::string& buffer) {
make_new_directory(current_path.string() + '/' + buffer);
load_directory_contents(current_path);
refresh_list();
add_children({
&menu_view,
&labels,
&text_date,
&button_rename,
&button_new_dir,
&button_delete
});
};
button_rename.on_select = [this, &nav](Button&) {
name_buffer = entry_list[menu_view.highlighted_index()].entry_path.filename().string().substr(0, max_filename_length);
on_rename(nav);
};
button_delete.on_select = [this, &nav](Button&) {
// Use display_modal ?
nav.push<ModalMessageView>("Delete", "Delete " + entry_list[menu_view.highlighted_index()].entry_path.filename().string() + "\nAre you sure?", YESNO,
[this](bool choice) {
if (choice)
on_delete();
}
);
};
menu_view.on_highlight = [this]() {
text_date.set(to_string_FAT_timestamp(file_created_date(get_selected_path())));
};
refresh_list();
on_select_entry = [this]() {
if (entry_list[menu_view.highlighted_index()].is_directory) {
load_directory_contents(get_selected_path());
refresh_list();
} else
button_rename.focus();
};
button_new_dir.on_select = [this, &nav](Button&) {
name_buffer.clear();
text_prompt(nav, name_buffer, max_filename_length, [this](std::string& buffer) {
make_new_directory(current_path.string() + '/' + buffer);
load_directory_contents(current_path);
refresh_list();
});
};
button_rename.on_select = [this, &nav](Button&) {
name_buffer = entry_list[menu_view.highlighted_index()].entry_path.filename().string().substr(0, max_filename_length);
on_rename(nav);
};
button_delete.on_select = [this, &nav](Button&) {
// Use display_modal ?
nav.push<ModalMessageView>("Delete", "Delete " + entry_list[menu_view.highlighted_index()].entry_path.filename().string() + "\nAre you sure?", YESNO,
[this](bool choice) {
if (choice)
on_delete();
}
);
};
}
}
}
+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,334 @@
/*
* 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 += SEARCH_SLICE_WIDTH; //Move into the next bandwidth slice NOTE: spectrum.sampling_rate = SEARCH_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 + (SEARCH_SLICE_WIDTH / 2); //Initial center frequency for sweep
f_center_ini += SEARCH_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(SEARCH_SLICE_WIDTH, field_trigger.value());
receiver_model.set_tuning_frequency(f_center_ini); //tune rx for this slice
}
void GlassView::PlotMarker(rf::Frequency pos)
{
pos = pos * 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({pos - 2, 100, 5, 3}, Color::red()); //Red marker middle
portapack::display.fill_rectangle({pos - 1, 103, 3, 3}, Color::red()); //Red marker middle
portapack::display.fill_rectangle({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) {
min_color_power = v;
};
range_presets.on_change = [this](size_t n, OptionsField::value_t v) {
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(SEARCH_SLICE_WIDTH, v);
};
display.scroll_set_area( 109, 319);
baseband::set_spectrum(SEARCH_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(SEARCH_SLICE_WIDTH); //20mhz
receiver_model.set_baseband_bandwidth(SEARCH_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,183 @@
/*
* 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 SEARCH_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();
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 { SEARCH_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);
}
}
}
};
};
}
+220 -19
View File
@@ -37,7 +37,17 @@ using namespace portapack;
namespace ui {
void MicTXView::focus() {
field_frequency.focus();
switch(focused_ui) {
case 0:
field_frequency.focus();
break;
case 1:
field_rxfrequency.focus();
break;
default:
field_va.focus();
break;
}
}
void MicTXView::update_vumeter() {
@@ -61,22 +71,25 @@ void MicTXView::configure_baseband() {
void MicTXView::set_tx(bool enable) {
if (enable) {
if (rx_enabled) //If audio RX is enabled
rxaudio(false); //Then turn off audio RX
transmitting = true;
configure_baseband();
transmitter_model.set_tuning_frequency(tx_frequency);
transmitter_model.set_tx_gain(tx_gain);
transmitter_model.set_rf_amp(rf_amp);
transmitter_model.enable();
portapack::pin_i2s0_rx_sda.mode(3); // This is already done in audio::init but gets changed by the CPLD overlay reprogramming
//gpio_tx.write(1);
//led_tx.on();
} else {
if (transmitting && rogerbeep_enabled) {
baseband::request_beep();
transmitting = false;
} else {
baseband::request_beep(); //Transmit the roger beep
transmitting = false; //And flag the end of the transmission so ...
} else { // (if roger beep was enabled, this will be executed after the beep ends transmitting.
transmitting = false;
configure_baseband();
transmitter_model.disable();
//gpio_tx.write(0);
//led_tx.off();
if (rx_enabled) //If audio RX is enabled and we've been transmitting
rxaudio(true); //Turn back on audio RX
}
}
}
@@ -113,13 +126,56 @@ void MicTXView::do_timing() {
} else {
// Check for PTT release
const auto switches_state = get_switches_state();
if (!switches_state[0] && transmitting) // Right button
if (!switches_state[4] && transmitting && !button_touch) // Select button
set_tx(false);
}
}
/* Hmmmm. Maybe useless now.
void MicTXView::on_tuning_frequency_changed(rf::Frequency f) {
transmitter_model.set_tuning_frequency(f);
//if ( rx_enabled )
receiver_model.set_tuning_frequency(f); //Update freq also for RX
}
*/
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);
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.
receiver_model.set_tuning_frequency(rx_frequency); // Now with seperate controls!
receiver_model.set_lna(rx_lna);
receiver_model.set_vga(rx_vga);
receiver_model.set_rf_amp(rx_amp);
receiver_model.enable();
audio::output::start();
} else { //These incredibly convoluted steps are required for the vumeter to reappear when stopping RX.
receiver_model.disable();
baseband::shutdown();
baseband::run_image(portapack::spi_flash::image_tag_mic_tx);
audio::input::start();
// transmitter_model.enable();
portapack::pin_i2s0_rx_sda.mode(3);
// transmitting = false;
configure_baseband();
// transmitter_model.disable();
}
}
void MicTXView::on_headphone_volume_changed(int32_t v) {
//if (rx_enabled) {
const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max;
receiver_model.set_headphone_volume(new_volume);
//}
}
void MicTXView::set_ptt_visibility(bool v) {
tx_button.hidden(!v);
}
MicTXView::MicTXView(
@@ -134,17 +190,27 @@ MicTXView::MicTXView(
&labels,
&vumeter,
&options_gain,
&check_va,
// &check_va,
&field_va,
&field_va_level,
&field_va_attack,
&field_va_decay,
&field_bw,
&field_rfgain,
&field_rfamp,
&field_frequency,
&options_tone_key,
&check_rogerbeep,
&text_ptt
&check_rxactive,
&field_volume,
&field_squelch,
&field_rxfrequency,
&field_rxlna,
&field_rxvga,
&field_rxamp,
&tx_button
});
tone_keys_populate(options_tone_key);
options_tone_key.on_change = [this](size_t i, int32_t) {
tone_key_index = i;
@@ -157,17 +223,24 @@ MicTXView::MicTXView(
};
options_gain.set_selected_index(1); // x1.0
tx_frequency = transmitter_model.tuning_frequency();
field_frequency.set_value(transmitter_model.tuning_frequency());
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_change = [this](rf::Frequency f) {
this->on_tuning_frequency_changed(f);
tx_frequency = f;
if(!rx_enabled)
transmitter_model.set_tuning_frequency(f);
};
field_frequency.on_edit = [this, &nav]() {
focused_ui = 0;
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
auto new_view = nav.push<FrequencyKeypadView>(tx_frequency);
new_view->on_changed = [this](rf::Frequency f) {
this->on_tuning_frequency_changed(f);
tx_frequency = f;
if(!rx_enabled)
transmitter_model.set_tuning_frequency(f);
this->field_frequency.set_value(f);
set_dirty();
};
};
@@ -176,18 +249,61 @@ MicTXView::MicTXView(
};
field_bw.set_value(10);
tx_gain = transmitter_model.tx_gain();
field_rfgain.on_change = [this](int32_t v) {
tx_gain = v;
};
field_rfgain.set_value(tx_gain);
rf_amp = transmitter_model.rf_amp();
field_rfamp.on_change = [this](int32_t v) {
rf_amp = (bool)v;
};
field_rfamp.set_value(rf_amp ? 14 : 0);
/*
check_va.on_select = [this](Checkbox&, bool v) {
va_enabled = v;
text_ptt.hidden(v);
text_ptt.hidden(v); //hide / show PTT text
check_rxactive.hidden(v); //hide / show the RX AUDIO
set_dirty(); //Refresh display
};
*/
field_va.set_selected_index(1);
field_va.on_change = [this](size_t, int32_t v) {
switch(v) {
case 0:
va_enabled = 0;
this->set_ptt_visibility(0);
check_rxactive.hidden(0);
ptt_enabled = 0;
break;
case 1:
va_enabled = 0;
this->set_ptt_visibility(1);
check_rxactive.hidden(0);
ptt_enabled = 1;
break;
case 2:
if (!rx_enabled) {
va_enabled = 1;
this->set_ptt_visibility(0);
check_rxactive.hidden(1);
ptt_enabled = 0;
} else {
field_va.set_selected_index(1);
}
break;
}
set_dirty();
};
check_va.set_value(false);
check_rogerbeep.on_select = [this](Checkbox&, bool v) {
rogerbeep_enabled = v;
};
check_rogerbeep.set_value(false);
field_va_level.on_change = [this](int32_t v) {
va_level = v;
vumeter.set_mark(v);
@@ -203,7 +319,89 @@ MicTXView::MicTXView(
decay_ms = v;
};
field_va_decay.set_value(1000);
check_rxactive.on_select = [this](Checkbox&, bool v) {
// vumeter.set_value(0); //Start with a clean vumeter
rx_enabled = v;
// check_va.hidden(v); //Hide or show voice activation
rxaudio(v); //Activate-Deactivate audio rx accordingly
set_dirty(); //Refresh interface
};
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_squelch.on_change = [this](int32_t v) {
receiver_model.set_squelch_level(100 - v);
};
field_squelch.set_value(0);
receiver_model.set_squelch_level(0);
rx_frequency = receiver_model.tuning_frequency();
field_rxfrequency.set_value(rx_frequency);
field_rxfrequency.set_step(receiver_model.frequency_step());
field_rxfrequency.on_change = [this](rf::Frequency f) {
rx_frequency = f;
if(rx_enabled)
receiver_model.set_tuning_frequency(f);
};
field_rxfrequency.on_edit = [this, &nav]() {
focused_ui = 1;
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(rx_frequency);
new_view->on_changed = [this](rf::Frequency f) {
rx_frequency = f;
if(rx_enabled)
receiver_model.set_tuning_frequency(f);
this->field_rxfrequency.set_value(f);
set_dirty();
};
};
rx_lna = receiver_model.lna();
field_rxlna.on_change = [this](int32_t v) {
rx_lna = v;
if(rx_enabled)
receiver_model.set_lna(v);
};
field_rxlna.set_value(rx_lna);
rx_vga = receiver_model.vga();
field_rxvga.on_change = [this](int32_t v) {
rx_vga = v;
if(rx_enabled)
receiver_model.set_vga(v);
};
field_rxvga.set_value(rx_vga);
rx_amp = receiver_model.rf_amp();
field_rxamp.on_change = [this](int32_t v) {
rx_amp = v;
if(rx_enabled)
receiver_model.set_rf_amp(rx_amp);
};
field_rxamp.set_value(rx_amp);
tx_button.on_select = [this](Button&) {
if(ptt_enabled && !transmitting) {
set_tx(true);
}
};
tx_button.on_touch_release = [this](Button&) {
if(button_touch) {
button_touch = false;
set_tx(false);
}
};
tx_button.on_touch_press = [this](Button&) {
if(!transmitting) {
button_touch = true;
}
};
transmitter_model.set_sampling_rate(sampling_rate);
transmitter_model.set_baseband_bandwidth(1750000);
@@ -215,7 +413,10 @@ MicTXView::MicTXView(
MicTXView::~MicTXView() {
audio::input::stop();
transmitter_model.set_tuning_frequency(tx_frequency); // Save Tx frequency instead of Rx. Or maybe we need some "System Wide" changes to seperate Tx and Rx frequency.
transmitter_model.disable();
if (rx_enabled) //Also turn off audio rx if enabled
rxaudio(false);
baseband::shutdown();
}
+142 -32
View File
@@ -30,6 +30,7 @@
#include "transmitter_model.hpp"
#include "tone_key.hpp"
#include "message.hpp"
#include "receiver_model.hpp"
namespace ui {
@@ -46,30 +47,40 @@ public:
void focus() override;
// PTT: Enable through KeyEvent (only works with presses), disable by polling :(
// This is the old "RIGHT BUTTON" method.
/*
bool on_key(const KeyEvent key) {
if ((key == KeyEvent::Right) && (!va_enabled)) {
if ((key == KeyEvent::Right) && (!va_enabled) && ptt_enabled) {
set_tx(true);
return true;
} else
return false;
};
std::string title() const override { return "Microphone TX"; };
*/
std::string title() const override { return "Mic TX RX"; };
private:
static constexpr uint32_t sampling_rate = 1536000U;
static constexpr uint32_t lcd_frame_duration = (256 * 1000UL) / 60; // 1 frame @ 60fps in ms .8 fixed point
static constexpr uint32_t lcd_frame_duration = (256 * 1000UL) / 60; // 1 frame @ 60fps in ms .8 fixed point /60
void update_vumeter();
void do_timing();
void set_tx(bool enable);
void on_tuning_frequency_changed(rf::Frequency f);
// void on_tuning_frequency_changed(rf::Frequency f);
void on_tx_progress(const bool done);
void configure_baseband();
void rxaudio(bool is_on);
void on_headphone_volume_changed(int32_t v);
void set_ptt_visibility(bool v);
bool transmitting { false };
bool va_enabled { };
bool rogerbeep_enabled { };
bool va_enabled { false };
bool ptt_enabled { true };
bool rogerbeep_enabled { false };
bool rx_enabled { false };
uint32_t tone_key_index { };
float mic_gain { 1.0 };
uint32_t audio_level { 0 };
@@ -78,25 +89,45 @@ private:
uint32_t decay_ms { };
uint32_t attack_timer { 0 };
uint32_t decay_timer { 0 };
int32_t tx_gain { 47 };
bool rf_amp { false };
int32_t rx_lna { 32 };
int32_t rx_vga { 32 };
bool rx_amp { false };
rf::Frequency tx_frequency { 0 };
rf::Frequency rx_frequency { 0 };
int32_t focused_ui { 2 };
bool button_touch { false };
Labels labels {
{ { 7 * 8, 1 * 8 }, "Mic. gain:", Color::light_grey() },
{ { 7 * 8, 4 * 8 }, "Frequency:", Color::light_grey() },
{ { 7 * 8, 6 * 8 }, "Bandwidth: kHz", Color::light_grey() },
{ { 9 * 8, 13 * 8 }, "Level: /255", Color::light_grey() },
{ { 9 * 8, 15 * 8 }, "Attack: ms", Color::light_grey() },
{ { 9 * 8, 17 * 8 }, "Decay: ms", Color::light_grey() },
{ { 7 * 8, 21 * 8 }, "Tone key:", Color::light_grey() }
{ { 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() },
{ { 3 * 8, 5 * 8 }, "GAIN:", Color::light_grey() },
{ {11 * 8, 5 * 8 }, "Amp:", 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, 25 * 8 }, "SQ:", Color::light_grey() },
{ { 5 * 8, 25 * 8 }, "F:", Color::light_grey() },
{ { 5 * 8, 27 * 8 }, "LNA:", Color::light_grey()},
{ {12 * 8, 27 * 8 }, "VGA:", Color::light_grey()},
{ {19 * 8, 27 * 8 }, "AMP:", Color::light_grey()}
};
VuMeter vumeter {
{ 1 * 8, 2 * 8, 5 * 8, 32 * 8 },
20,
false
{ 0 * 8, 1 * 8, 2 * 8, 33 * 8 },
12,
true
};
OptionsField options_gain {
{ 17 * 8, 1 * 8 },
{ 13 * 8, 1 * 8 },
4,
{
{ "x0.5", 5 },
@@ -107,39 +138,65 @@ private:
};
FrequencyField field_frequency {
{ 17 * 8, 4 * 8 },
{ 5 * 8, 3 * 8 },
};
NumberField field_bw {
{ 17 * 8, 6 * 8 },
{ 18 * 8, 3 * 8 },
3,
{ 0, 150 },
1,
' '
};
NumberField field_rfgain {
{ 8 * 8, 5 * 8 },
2,
{ 0, 47 },
1,
' '
};
NumberField field_rfamp {
{ 15 * 8, 5 * 8 },
2,
{ 0, 14 },
14,
' '
};
/*
Checkbox check_va {
{ 7 * 8, 10 * 8 },
{ 3 * 8, (10 * 8) - 4 },
7,
"Voice activation",
false
};
*/
OptionsField field_va {
{ 17 * 8, 8 * 8 },
3,
{
{" OFF", 0},
{" PTT", 1},
{"AUTO", 2}
}
};
NumberField field_va_level {
{ 15 * 8, 13 * 8 },
{ 8 * 8, 10 * 8 },
3,
{ 0, 255 },
2,
' '
};
NumberField field_va_attack {
{ 16 * 8, 15 * 8 },
{ 16 * 8, 10 * 8 },
3,
{ 0, 999 },
20,
' '
};
NumberField field_va_decay {
{ 15 * 8, 17 * 8 },
{ 24 * 8, 10 * 8 },
4,
{ 0, 9999 },
100,
@@ -147,28 +204,81 @@ private:
};
OptionsField options_tone_key {
{ 7 * 8, 23 * 8 },
{ 10 * 8, ( 15 * 8 ) - 2 },
23,
{ }
};
Checkbox check_rogerbeep {
{ 7 * 8, 26 * 8 },
{ 3 * 8, ( 16 * 8 ) + 4 },
10,
"Roger beep",
false
};
Text text_ptt {
{ 7 * 8, 17 * 16, 16 * 8, 16 },
"PTT: RIGHT BUTTON"
Checkbox check_rxactive {
{ 3 * 8, ( 21 * 8 ) - 4 },
8,
"RX audio listening",
false
};
NumberField field_volume {
{ 13 * 8, 23 * 8 },
2,
{ 0, 99 },
1,
' ',
};
NumberField field_squelch {
{ 20 * 8, 25 * 8 },
2,
{ 0, 99 },
1,
' ',
};
FrequencyField field_rxfrequency {
{ 7 * 8, 25 * 8 },
};
NumberField field_rxlna {
{ 9 * 8, 27 * 8 },
2,
{ 0, 40 },
8,
' ',
};
NumberField field_rxvga {
{ 16 * 8, 27 * 8 },
2,
{ 0, 62 },
2,
' ',
};
NumberField field_rxamp {
{ 23 * 8, 27 * 8 },
1,
{ 0, 1 },
1,
' ',
};
Button tx_button {
{ 10 * 8, 30 * 8, 10 * 8, 5 * 8 },
"TX",
true
};
MessageHandlerRegistration message_handler_lcd_sync {
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->update_vumeter();
this->do_timing();
this->update_vumeter();
}
};
+63 -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,12 @@ 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) {
loop = n;
};
field_time_unit.on_change = [this](int32_t) {
field_speed.on_change = [this](int32_t) {
update_tx_duration();
};
@@ -199,12 +245,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 },
+1 -1
View File
@@ -50,7 +50,7 @@ public:
void focus() override;
std::string title() const override { return "Numbers station"; };
std::string title() const override { return "Station"; };
private:
NavigationView& nav_;
+457 -112
View File
@@ -21,10 +21,7 @@
*/
#include "ui_scanner.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "audio.hpp"
#include "ui_fileman.hpp"
using namespace portapack;
@@ -38,6 +35,10 @@ ScannerThread::ScannerThread(
}
ScannerThread::~ScannerThread() {
stop();
}
void ScannerThread::stop() {
if( thread ) {
chThdTerminate(thread);
chThdWait(thread);
@@ -49,6 +50,28 @@ void ScannerThread::set_scanning(const bool v) {
_scanning = v;
}
bool ScannerThread::is_scanning() {
return _scanning;
}
void ScannerThread::set_freq_lock(const uint32_t v) {
_freq_lock = v;
}
uint32_t ScannerThread::is_freq_lock() {
return _freq_lock;
}
void ScannerThread::set_freq_del(const uint32_t v) {
_freq_del = v;
}
void ScannerThread::change_scanning_direction() {
_fwd = !_fwd;
chThdSleepMilliseconds(300); //Give some pause after reversing scanning direction
}
msg_t ScannerThread::static_fn(void* arg) {
auto obj = static_cast<ScannerThread*>(arg);
obj->run();
@@ -56,36 +79,78 @@ msg_t ScannerThread::static_fn(void* arg) {
}
void ScannerThread::run() {
RetuneMessage message { };
uint32_t frequency_index = 0;
while( !chThdShouldTerminate() ) {
if (_scanning) {
// Retune
receiver_model.set_tuning_frequency(frequency_list_[frequency_index]);
message.range = frequency_index;
EventDispatcher::send_message(message);
frequency_index++;
if (frequency_index >= frequency_list_.size())
frequency_index = 0;
if (frequency_list_.size()) { //IF THERE IS A FREQUENCY LIST ...
RetuneMessage message { };
uint32_t frequency_index = frequency_list_.size();
bool restart_scan = false; //Flag whenever scanning is restarting after a pause
while( !chThdShouldTerminate() ) {
if (_scanning) { //Scanning
if (_freq_lock == 0) { //normal scanning (not performing freq_lock)
if (!restart_scan) { //looping at full speed
if (_fwd) { //forward
frequency_index++;
if (frequency_index >= frequency_list_.size())
frequency_index = 0;
} else { //reverse
if (frequency_index < 1)
frequency_index = frequency_list_.size();
frequency_index--;
}
receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); // Retune
}
else
restart_scan=false; //Effectively skipping first retuning, giving system time
}
message.range = frequency_index; //Inform freq (for coloring purposes also!)
EventDispatcher::send_message(message);
}
else { //NOT scanning
if (_freq_del != 0) { //There is a frequency to delete
for (uint16_t i = 0; i < frequency_list_.size(); i++) { //Search for the freq to delete
if (frequency_list_[i] == _freq_del)
{ //found: Erase it
frequency_list_.erase(frequency_list_.begin() + i);
if (i==0) //set scan index one place back to compensate
i=frequency_list_.size();
else
i--;
break;
}
}
_freq_del = 0; //deleted.
}
else {
restart_scan=true; //Flag the need for skipping a cycle when restarting scan
}
}
chThdSleepMilliseconds(50); //Needed to (eventually) stabilize the receiver into new freq
}
chThdSleepMilliseconds(50);
}
}
void ScannerView::handle_retune(uint32_t i) {
text_cycle.set( to_string_dec_uint(i) + "/" +
to_string_dec_uint(frequency_list.size()) + " : " +
to_string_dec_uint(frequency_list[i]) );
desc_cycle.set( description_list[i] );
switch (scan_thread->is_freq_lock())
{
case 0: //NO FREQ LOCK, ONGOING STANDARD SCANNING
text_cycle.set( to_string_dec_uint(i + 1,3) );
current_index = i; //since it is an ongoing scan, this is a new index
if (description_list[current_index].size() > 0) desc_cycle.set( description_list[current_index] ); //Show new description
break;
case 1: //STARTING LOCK FREQ
big_display.set_style(&style_yellow);
break;
case MAX_FREQ_LOCK: //FREQ IS STRONG: GREEN and scanner will pause when on_statistics_update()
big_display.set_style(&style_green);
break;
default: //freq lock is checking the signal, do not update display
return;
}
big_display.set(frequency_list[current_index]); //UPDATE the big Freq after 0, 1 or MAX_FREQ_LOCK (at least, for color synching)
}
void ScannerView::focus() {
field_lna.focus();
field_mode.focus();
}
ScannerView::~ScannerView() {
@@ -94,9 +159,20 @@ ScannerView::~ScannerView() {
baseband::shutdown();
}
void ScannerView::show_max() { //show total number of freqs to scan
if (frequency_list.size() == MAX_DB_ENTRY) {
text_max.set_style(&style_red);
text_max.set( "/ " + to_string_dec_uint(MAX_DB_ENTRY) + " (DB MAX!)");
}
else {
text_max.set_style(&style_grey);
text_max.set( "/ " + to_string_dec_uint(frequency_list.size()));
}
}
ScannerView::ScannerView(
NavigationView&
)
NavigationView& nav
) : nav_ { nav }
{
add_children({
&labels,
@@ -105,110 +181,379 @@ ScannerView::ScannerView(
&field_rf_amp,
&field_volume,
&field_bw,
&field_trigger,
&field_squelch,
&field_wait,
//&record_view,
&button_load,
&rssi,
&text_cycle,
&text_max,
&desc_cycle,
//&waterfall,
&big_display,
&button_manual_start,
&button_manual_end,
&field_mode,
&step_mode,
&button_manual_scan,
&button_pause,
&button_dir,
&button_audio_app,
&button_mic_app,
&button_add,
&button_remove
});
std::string scanner_file = "SCANNER";
if (load_freqman_file(scanner_file, database)) {
for(auto& entry : database) {
// FIXME
if (entry.type == RANGE) {
for (uint32_t i=entry.frequency_a; i < entry.frequency_b; i+= 1000000) {
frequency_list.push_back(i);
description_list.push_back("RNG " + to_string_dec_uint(entry.frequency_a) + ">" + to_string_dec_uint(entry.frequency_b));
}
def_step = change_mode(AM); //Start on AM
field_mode.set_by_value(AM); //Reflect the mode into the manual selector
//HELPER: Pre-setting a manual range, based on stored frequency
rf::Frequency stored_freq = persistent_memory::tuned_frequency();
frequency_range.min = stored_freq - 1000000;
button_manual_start.set_text(to_string_short_freq(frequency_range.min));
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 {
frequency_list.push_back(entry.frequency_a);
description_list.push_back(entry.description);
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) {
frequency_range.min = f;
button_manual_start.set_text(to_string_short_freq(f));
};
};
button_manual_end.on_select = [this, &nav](Button& button) {
auto new_view = nav.push<FrequencyKeypadView>(frequency_range.max);
new_view->on_changed = [this, &button](rf::Frequency f) {
frequency_range.max = f;
button_manual_end.set_text(to_string_short_freq(f));
};
};
button_pause.on_select = [this](Button&) {
if ( userpause )
user_resume();
else {
scan_pause();
button_pause.set_text("RESUME"); //PAUSED, show resume
userpause=true;
}
} else {
// DEBUG
// TODO: Clean this
frequency_list.push_back(466025000);
description_list.push_back("POCSAG-France");
frequency_list.push_back(466050000);
description_list.push_back("POCSAG-France");
frequency_list.push_back(466075000);
description_list.push_back("POCSAG-France");
frequency_list.push_back(466175000);
description_list.push_back("POCSAG-France");
frequency_list.push_back(466206250);
description_list.push_back("POCSAG-France");
frequency_list.push_back(466231250);
description_list.push_back("POCSAG-France");
};
button_audio_app.on_select = [this](Button&) {
scan_thread->stop();
nav_.pop();
nav_.push<AnalogAudioView>();
};
button_mic_app.on_select = [this](Button&) {
scan_thread->stop();
nav_.pop();
nav_.push<MicTXView>();
};
button_remove.on_select = [this](Button&) {
if (frequency_list.size() > current_index) {
if (scan_thread->is_scanning()) //STOP Scanning if necessary
scan_thread->set_scanning(false);
scan_thread->set_freq_del(frequency_list[current_index]);
description_list.erase(description_list.begin() + current_index);
frequency_list.erase(frequency_list.begin() + current_index);
show_max(); //UPDATE new list size on screen
desc_cycle.set(" "); //Clean up description (cosmetic detail)
scan_thread->set_freq_lock(0); //Reset the scanner lock
if ( userpause ) //If user-paused, resume
user_resume();
}
};
button_manual_scan.on_select = [this](Button&) {
if (!frequency_range.min || !frequency_range.max) {
nav_.display_modal("Error", "Both START and END freqs\nneed a value");
} else if (frequency_range.min > frequency_range.max) {
nav_.display_modal("Error", "END freq\nis lower than START");
} else {
audio::output::stop();
scan_thread->stop(); //STOP SCANNER THREAD
frequency_list.clear();
description_list.clear();
def_step = step_mode.selected_index_value(); //Use def_step from manual selector
description_list.push_back(
"M" + to_string_short_freq(frequency_range.min) + ">"
+ to_string_short_freq(frequency_range.max) + "S"
+ to_string_short_freq(def_step).erase(0,1) //euquiq: lame kludge to reduce spacing in step freq
);
rf::Frequency frequency = frequency_range.min;
while (frequency_list.size() < MAX_DB_ENTRY && frequency <= frequency_range.max) { //add manual range
frequency_list.push_back(frequency);
description_list.push_back(""); //If empty, will keep showing the last description
frequency+=def_step;
}
show_max();
if ( userpause ) //If user-paused, resume
user_resume();
big_display.set_style(&style_grey); //Back to grey color
start_scan_thread(); //RESTART SCANNER THREAD
}
};
field_mode.on_change = [this](size_t, OptionsField::value_t v) {
receiver_model.disable();
baseband::shutdown();
change_mode(v);
if ( !scan_thread->is_scanning() ) //for some motive, audio output gets stopped.
audio::output::start(); //So if scan was stopped we resume audio
receiver_model.enable();
};
button_dir.on_select = [this](Button&) {
scan_thread->change_scanning_direction();
if ( userpause ) //If user-paused, resume
user_resume();
big_display.set_style(&style_grey); //Back to grey color
};
button_add.on_select = [this](Button&) { //frequency_list[current_index]
File scanner_file;
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)
+ to_string_dec_uint(frequency_list[current_index] % 1000UL, 3, '0');
char one_char[1]; //Read it char by char
std::string line; //and put read line in here
bool found=false;
for (size_t pointer=0; pointer < scanner_file.size();pointer++) {
scanner_file.seek(pointer);
scanner_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
if (line.compare(0, frequency_to_add.size(),frequency_to_add) == 0) {
found=true;
break;
}
line.clear(); //Ready for next textline
}
}
if (found) {
nav_.display_modal("Error", "Frequency already exists");
big_display.set(frequency_list[current_index]); //After showing an error
}
else {
auto result = 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 " + loaded_file_name + ".TXT\nfor appending freq.");
big_display.set(frequency_list[current_index]); //After showing an error
}
};
//PRE-CONFIGURATION:
field_wait.on_change = [this](int32_t v) { wait = v; }; field_wait.set_value(5);
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";
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
}
field_bw.set_selected_index(2);
field_bw.on_change = [this](size_t n, OptionsField::value_t) {
receiver_model.set_nbfm_configuration(n);
};
field_wait.on_change = [this](int32_t v) {
wait = v;
};
field_wait.set_value(5);
field_trigger.on_change = [this](int32_t v) {
trigger = v;
};
field_trigger.set_value(30);
field_squelch.set_value(receiver_model.squelch_level());
field_squelch.on_change = [this](int32_t v) {
squelch = v;
receiver_model.set_squelch_level(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);
};
audio::output::start();
audio::output::mute();
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.enable();
receiver_model.set_squelch_level(0);
receiver_model.set_nbfm_configuration(field_bw.selected_index());
audio::output::unmute();
// TODO: Scanning thread here
scan_thread = std::make_unique<ScannerThread>(frequency_list);
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 ;
}
frequency_list.push_back(entry.frequency_a); //Store starting freq and description
description_list.push_back("R" + to_string_short_freq(entry.frequency_a)
+ ">" + to_string_short_freq(entry.frequency_b)
+ " S" + to_string_short_freq(def_step).erase(0,1) //euquiq: lame kludge to reduce spacing in step freq
);
while (frequency_list.size() < MAX_DB_ENTRY && entry.frequency_a <= entry.frequency_b) { //add the rest of the range
entry.frequency_a+=def_step;
frequency_list.push_back(entry.frequency_a);
description_list.push_back(""); //Token (keep showing the last description)
}
} else if ( entry.type == SINGLE) {
frequency_list.push_back(entry.frequency_a);
description_list.push_back("S: " + entry.description);
}
show_max();
}
else
{
break; //No more space: Stop reading the txt file !
}
}
}
else
{
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
start_scan_thread();
}
void ScannerView::on_statistics_update(const ChannelStatistics& statistics) {
int32_t max_db = statistics.max_db;
if (timer <= wait)
timer++;
if (max_db < -trigger) {
if (timer == wait) {
//audio::output::stop();
scan_thread->set_scanning(true);
if ( !userpause ) //Scanning not user-paused
{
if (timer >= (wait * 10) )
{
timer = 0;
scan_resume();
}
else if (!timer)
{
if (statistics.max_db > squelch ) { //There is something on the air...(statistics.max_db > -squelch)
if (scan_thread->is_freq_lock() >= MAX_FREQ_LOCK) { //checking time reached
scan_pause();
timer++;
} else {
scan_thread->set_freq_lock( scan_thread->is_freq_lock() + 1 ); //in lock period, still analyzing the signal
}
} else { //There is NOTHING on the air
if (scan_thread->is_freq_lock() > 0) { //But are we already in freq_lock ?
big_display.set_style(&style_grey); //Back to grey color
scan_thread->set_freq_lock(0); //Reset the scanner lock, since there is no signal
}
}
}
else //Ongoing wait time
{
timer++;
}
} else {
//audio::output::start();
scan_thread->set_scanning(false);
timer = 0;
}
}
void ScannerView::scan_pause() {
if (scan_thread->is_scanning()) {
scan_thread->set_freq_lock(0); //Reset the scanner lock (because user paused, or MAX_FREQ_LOCK reached) for next freq scan
scan_thread->set_scanning(false); // WE STOP SCANNING
audio::output::start();
}
}
void ScannerView::scan_resume() {
audio::output::stop();
big_display.set_style(&style_grey); //Back to grey color
if (!scan_thread->is_scanning())
scan_thread->set_scanning(true); // RESUME!
}
void ScannerView::user_resume() {
timer = wait * 10; //Will trigger a scan_resume() on_statistics_update, also advancing to next freq.
button_pause.set_text("PAUSE"); //Show button for pause
userpause=false; //Resume scanning
}
void ScannerView::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);
}
} /* namespace ui */
size_t ScannerView::change_mode(uint8_t new_mod) { //Before this, do a scan_thread->stop(); After this do a start_scan_thread()
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) { };
switch (new_mod) {
case NFM: //bw 16k (2) default
bw.emplace_back("8k5", 0);
bw.emplace_back("11k", 0);
bw.emplace_back("16k", 0);
field_bw.set_options(bw);
baseband::run_image(portapack::spi_flash::image_tag_nfm_audio);
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
field_bw.set_selected_index(2);
receiver_model.set_nbfm_configuration(field_bw.selected_index());
field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_nbfm_configuration(n); };
receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(1750000);
break;
case AM:
bw.emplace_back("DSB", 0);
bw.emplace_back("USB", 0);
bw.emplace_back("LSB", 0);
bw.emplace_back("CW ", 0);
field_bw.set_options(bw);
baseband::run_image(portapack::spi_flash::image_tag_am_audio);
receiver_model.set_modulation(ReceiverModel::Mode::AMAudio);
field_bw.set_selected_index(0);
receiver_model.set_am_configuration(field_bw.selected_index());
field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_am_configuration(n); };
receiver_model.set_sampling_rate(2000000);receiver_model.set_baseband_bandwidth(2000000);
break;
case WFM:
bw.emplace_back("16k", 0);
field_bw.set_options(bw);
baseband::run_image(portapack::spi_flash::image_tag_wfm_audio);
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
field_bw.set_selected_index(0);
receiver_model.set_wfm_configuration(field_bw.selected_index());
field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_wfm_configuration(n); };
receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(2000000);
break;
}
return mod_step[new_mod];
}
void ScannerView::start_scan_thread() {
receiver_model.enable();
receiver_model.set_squelch_level(0);
scan_thread = std::make_unique<ScannerThread>(frequency_list);
}
} /* namespace ui */
+189 -41
View File
@@ -20,20 +20,46 @@
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "receiver_model.hpp"
#include "ui_receiver.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "freqman.hpp"
#include "analog_audio_app.hpp"
#include "audio.hpp"
#include "ui_mictx.hpp"
#include "portapack_persistent_memory.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "file.hpp"
#define MAX_DB_ENTRY 500
#define MAX_FREQ_LOCK 10 //50ms cycles scanner locks into freq when signal detected, to verify signal is not spureous
namespace ui {
enum modulation_type { AM = 0,WFM,NFM };
string const mod_name[3] = {"AM", "WFM", "NFM"};
size_t const mod_step[3] = {9000, 100000, 12500 };
class ScannerThread {
public:
ScannerThread(std::vector<rf::Frequency> frequency_list);
~ScannerThread();
void set_scanning(const bool v);
bool is_scanning();
void set_freq_lock(const uint32_t v);
uint32_t is_freq_lock();
void set_freq_del(const uint32_t v);
void change_scanning_direction();
void stop();
ScannerThread(const ScannerThread&) = delete;
ScannerThread(ScannerThread&&) = delete;
@@ -45,38 +71,83 @@ private:
Thread* thread { nullptr };
bool _scanning { true };
bool _fwd { true };
uint32_t _freq_lock { 0 };
uint32_t _freq_del { 0 };
static msg_t static_fn(void* arg);
void run();
};
class ScannerView : public View {
public:
ScannerView(NavigationView&);
ScannerView(NavigationView& nav);
~ScannerView();
void focus() override;
void big_display_freq(rf::Frequency f);
const Style style_grey { // scanning
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::grey(),
};
std::string title() const override { return "Scanner"; };
const Style style_yellow { //Found signal
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::dark_yellow(),
};
const Style style_green { //Found signal
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::green(),
};
const Style style_red { //erasing freq
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::red(),
};
std::string title() const override { return "SCANNER"; };
std::vector<rf::Frequency> frequency_list{ };
std::vector<string> description_list { };
//void set_parent_rect(const Rect new_parent_rect) override;
private:
NavigationView& nav_;
void start_scan_thread();
size_t change_mode(uint8_t mod_type);
void show_max();
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);
void handle_retune(uint32_t i);
std::vector<rf::Frequency> frequency_list { };
std::vector<string> description_list { };
int32_t trigger { 0 };
jammer::jammer_range_t frequency_range { false, 0, 0 }; //perfect for manual scan task too...
int32_t squelch { 0 };
uint32_t timer { 0 };
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 };
Labels labels {
{ { 0 * 8, 0 * 16 }, "LNA: TRIGGER: /99 VOL:", Color::light_grey() },
{ { 0 * 8, 1 * 16 }, "VGA: SQUELCH: /99 AMP:", Color::light_grey() },
{ { 0 * 8, 2 * 16 }, " BW: WAIT:", Color::light_grey() },
{ { 0 * 8, 0 * 16 }, "LNA: VGA: AMP: VOL:", Color::light_grey() },
{ { 0 * 8, 1* 16 }, "BW: SQUELCH: db WAIT:", Color::light_grey() },
{ { 3 * 8, 10 * 16 }, "START END MANUAL", Color::light_grey() },
{ { 0 * 8, (26 * 8) + 4 }, "MODE:", Color::light_grey() },
{ { 11 * 8, (26 * 8) + 4 }, "STEP:", Color::light_grey() },
};
LNAGainField field_lna {
@@ -84,15 +155,15 @@ private:
};
VGAGainField field_vga {
{ 4 * 8, 1 * 16 }
{ 11 * 8, 0 * 16 }
};
RFAmpField field_rf_amp {
{ 28 * 8, 1 * 16 }
{ 18 * 8, 0 * 16 }
};
NumberField field_volume {
{ 28 * 8, 0 * 16 },
{ 24 * 8, 0 * 16 },
2,
{ 0, 99 },
1,
@@ -100,46 +171,123 @@ private:
};
OptionsField field_bw {
{ 4 * 8, 2 * 16 },
3,
{
{ "8k5", 0 },
{ "11k", 0 },
{ "16k", 0 },
}
};
{ 3 * 8, 1 * 16 },
4,
{ }
};
NumberField field_trigger {
{ 16 * 8, 0 * 16 },
2,
{ 0, 99 },
1,
' ',
};
NumberField field_squelch {
{ 16 * 8, 1 * 16 },
2,
{ 0, 99 },
{ 15 * 8, 1 * 16 },
3,
{ -90, 20 },
1,
' ',
};
NumberField field_wait {
{ 16 * 8, 2 * 16 },
{ 26 * 8, 1 * 16 },
2,
{ 0, 99 },
1,
' ',
};
RSSI rssi {
{ 0 * 16, 2 * 16, 15 * 16, 8 },
};
Text text_cycle {
{ 0, 5 * 16, 240, 16 },
"--/--"
{ 0, 3 * 16, 3 * 8, 16 },
};
Text text_max {
{ 4 * 8, 3 * 16, 18 * 8, 16 },
};
Text desc_cycle {
{0, 6 * 16, 240, 16 },
" "
{0, 4 * 16, 240, 16 },
};
BigFrequency big_display { //Show frequency in glamour
{ 4, 6 * 16, 28 * 8, 52 },
0
};
Button button_manual_start {
{ 0 * 8, 11 * 16, 11 * 8, 28 },
""
};
Button button_manual_end {
{ 12 * 8, 11 * 16, 11 * 8, 28 },
""
};
Button button_manual_scan {
{ 24 * 8, 11 * 16, 6 * 8, 28 },
"SCAN"
};
OptionsField field_mode {
{ 5 * 8, (26 * 8) + 4 },
6,
{
{ " AM ", 0 },
{ " WFM ", 1 },
{ " NFM ", 2 },
}
};
OptionsField step_mode {
{ 17 * 8, (26 * 8) + 4 },
12,
{
{ "5Khz (SA AM)", 5000 },
{ "9Khz (EU AM)", 9000 },
{ "10Khz(US AM)", 10000 },
{ "50Khz (FM1)", 50000 },
{ "100Khz(FM2)", 100000 },
{ "6.25khz(NFM)", 6250 },
{ "12.5khz(NFM)", 12500 },
{ "25khz (N1)", 25000 },
{ "250khz (N2)", 250000 },
{ "8.33khz(AIR)", 8330 }
}
};
Button button_pause {
{ 0, (15 * 16) - 4, 72, 28 },
"PAUSE"
};
Button button_dir {
{ 0, (35 * 8) - 4, 72, 28 },
"FW><RV"
};
Button button_audio_app {
{ 84, (15 * 16) - 4, 72, 28 },
"AUDIO"
};
Button button_mic_app {
{ 84, (35 * 8) - 4, 72, 28 },
"MIC TX"
};
Button button_add {
{ 168, (15 * 16) - 4, 72, 28 },
"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"
};
std::unique_ptr<ScannerThread> scan_thread { };
@@ -160,4 +308,4 @@ private:
};
};
} /* namespace ui */
} /* namespace ui */
+40
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,
@@ -156,6 +160,39 @@ SetRadioView::SetRadioView(
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()),
};
}
+28 -3
View File
@@ -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,
+11 -3
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() {
@@ -52,7 +56,7 @@ void SigGenView::update_tone() {
void SigGenView::start_tx() {
transmitter_model.set_sampling_rate(1536000);
transmitter_model.set_rf_amp(true);
// transmitter_model.set_rf_amp(true);
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable();
@@ -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)
+1 -1
View File
@@ -40,7 +40,7 @@ public:
void focus() override;
std::string title() const override { return "Signal generator"; };
std::string title() const override { return "Generator"; };
private:
void start_tx();
+59 -26
View File
@@ -24,9 +24,14 @@
#include "baseband_api.hpp"
#include "portapack.hpp"
#include <cstring>
#include <stdio.h>
using namespace portapack;
#include "string_format.hpp"
#include "complex.hpp"
void SondeLogger::on_packet(const sonde::Packet& packet) {
const auto formatted = packet.symbols_formatted();
@@ -46,15 +51,20 @@ SondeView::SondeView(NavigationView& nav) {
&field_vga,
&rssi,
&check_log,
&check_crc,
&text_signature,
&text_serial,
&text_timestamp,
&text_voltage,
&text_frame,
&text_temp,
&text_humid,
&geopos,
&button_see_map
});
field_frequency.set_value(target_frequency_);
field_frequency.set_step(10000);
field_frequency.set_step(500); //euquiq: was 10000, but we are using this for fine-tunning
field_frequency.on_change = [this](rf::Frequency f) {
set_target_frequency(f);
field_frequency.set_value(f);
@@ -73,6 +83,10 @@ SondeView::SondeView(NavigationView& nav) {
check_log.on_select = [this](Checkbox&, bool v) {
logging = v;
};
check_crc.on_select = [this](Checkbox&, bool v) {
use_crc = v;
};
radio::enable({
tuning_frequency(),
@@ -86,12 +100,12 @@ SondeView::SondeView(NavigationView& nav) {
button_see_map.on_select = [this, &nav](Button&) {
nav.push<GeoMapView>(
"",
altitude,
sonde_id,
gps_info.alt,
GeoPos::alt_unit::METERS,
latitude,
longitude,
0);
gps_info.lat,
gps_info.lon,
999); //set a dummy heading out of range to draw a cross...probably not ideal?
};
logger = std::make_unique<SondeLogger>();
@@ -109,27 +123,46 @@ void SondeView::focus() {
field_vga.focus();
}
void SondeView::on_packet(const sonde::Packet& packet) {
//const auto hex_formatted = packet.symbols_formatted();
text_signature.set(packet.type_string());
text_serial.set(packet.serial_number());
text_voltage.set(unit_auto_scale(packet.battery_voltage(), 2, 3) + "V");
altitude = packet.GPS_altitude();
latitude = packet.GPS_latitude();
longitude = packet.GPS_longitude();
geopos.set_altitude(altitude);
geopos.set_lat(latitude);
geopos.set_lon(longitude);
if (logger && logging) {
logger->on_packet(packet);
void SondeView::on_packet(const sonde::Packet &packet)
{
if (!use_crc || packet.crc_ok()) //euquiq: Reject bad packet if crc is on
{
text_signature.set(packet.type_string());
sonde_id = packet.serial_number(); //used also as tag on the geomap
text_serial.set(sonde_id);
text_timestamp.set(to_string_timestamp(packet.received_at()));
text_voltage.set(unit_auto_scale(packet.battery_voltage(), 2, 2) + "V");
text_frame.set(to_string_dec_uint(packet.frame(),0)); //euquiq: integrate frame #, temp & humid.
temp_humid_info = packet.get_temp_humid();
if (temp_humid_info.humid != 0)
{
double decimals = abs(get_decimals(temp_humid_info.humid, 10, true));
//if (decimals < 0)
// decimals = -decimals;
text_humid.set(to_string_dec_int((int)temp_humid_info.humid) + "." + to_string_dec_uint(decimals, 1) + "%");
}
if (temp_humid_info.temp != 0)
{
double decimals = abs(get_decimals(temp_humid_info.temp, 10, true));
// if (decimals < 0)
// decimals = -decimals;
text_temp.set(to_string_dec_int((int)temp_humid_info.temp) + "." + to_string_dec_uint(decimals, 1) + "C");
}
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)
logger->on_packet(packet);
}
/*if( packet.crc_ok() ) {
}*/
}
void SondeView::set_target_frequency(const uint32_t new_value) {
+56 -21
View File
@@ -65,21 +65,28 @@ public:
private:
std::unique_ptr<SondeLogger> logger { };
uint32_t target_frequency_ { 402000000 };
uint32_t target_frequency_ { 402700000 };
bool logging { false };
int32_t altitude { 0 };
float latitude { 0 };
float longitude { 0 };
bool use_crc { false };
sonde::GPS_data gps_info;
sonde::temp_humid temp_humid_info;
std::string sonde_id;
Labels labels {
{ { 0 * 8, 2 * 16 }, "Signature:", Color::light_grey() },
{ { 3 * 8, 3 * 16 }, "Serial:", Color::light_grey() },
{ { 4 * 8, 4 * 16 }, "Vbatt:", Color::light_grey() }
{ { 4 * 8, 2 * 16 }, "Type:", Color::light_grey() },
{ { 6 * 8, 3 * 16 }, "ID:", Color::light_grey() },
{ { 0 * 8, 4 * 16 }, "DateTime:", Color::light_grey() },
{ { 3 * 8, 5 * 16 }, "Vbatt:", Color::light_grey() },
{ { 3 * 8, 6 * 16 }, "Frame:", Color::light_grey() },
{ { 4 * 8, 7 * 16 }, "Temp:", Color::light_grey() },
{ { 0 * 8, 8 * 16 }, "Humidity:", Color::light_grey() }
};
FrequencyField field_frequency {
{ 0 * 8, 0 * 8 },
};
RFAmpField field_rf_amp {
{ 13 * 8, 0 * 16 }
};
@@ -97,31 +104,59 @@ private:
};
Checkbox check_log {
{ 22 * 8, 3 * 16 },
{ 23 * 8, 6 * 16 },
3,
"Log"
};
Text text_signature {
{ 10 * 8, 2 * 16, 10 * 8, 16 },
"..."
};
Text text_serial {
{ 10 * 8, 3 * 16, 11 * 8, 16 },
"..."
};
Text text_voltage {
{ 10 * 8, 4 * 16, 10 * 8, 16 },
"..."
Checkbox check_crc {
{ 23 * 8, 8 * 16 },
3,
"CRC"
};
Text text_signature {
{ 9 * 8, 2 * 16, 10 * 8, 16 },
"..."
};
Text text_serial {
{ 9 * 8, 3 * 16, 11 * 8, 16 },
"..."
};
Text text_timestamp {
{ 9 * 8, 4 * 16, 11 * 8, 16 },
"..."
};
Text text_voltage {
{ 9 * 8, 5 * 16, 10 * 8, 16 },
"..."
};
Text text_frame {
{ 9 * 8, 6 * 16, 10 * 8, 16 },
"..."
};
Text text_temp {
{ 9 * 8, 7 * 16, 10 * 8, 16 },
"..."
};
Text text_humid {
{ 9 * 8, 8 * 16, 10 * 8, 16 },
"..."
};
GeoPos geopos {
{ 0, 6 * 16 },
{ 0, 12 * 16 },
GeoPos::alt_unit::METERS
};
Button button_see_map {
{ 8 * 8, 10 * 16, 14 * 8, 3 * 16 },
{ 8 * 8, 16 * 16, 14 * 8, 3 * 16 },
"See on map"
};
+9 -11
View File
@@ -81,15 +81,6 @@ void ViewWavView::load_wav(std::filesystem::path file_path) {
int16_t sample;
uint32_t average;
if (!wav_reader->open(file_path)) {
nav_.display_modal("Error", "Couldn't open file.", INFO, nullptr);
return;
}
if ((wav_reader->channels() != 1) || (wav_reader->bits_per_sample() != 16)) {
nav_.display_modal("Error", "Wrong format.\nWav viewer only accepts\n16-bit mono files.", INFO, nullptr);
return;
}
text_filename.set(file_path.filename().string());
auto ms_duration = wav_reader->ms_duration();
@@ -148,10 +139,18 @@ ViewWavView::ViewWavView(
&field_cursor_b,
&text_delta
});
reset_controls();
button_open.on_select = [this, &nav](Button&) {
auto open_view = nav.push<FileLoadView>(".WAV");
open_view->on_changed = [this](std::filesystem::path file_path) {
if (!wav_reader->open(file_path)) {
nav_.display_modal("Error", "Couldn't open file.", INFO, nullptr);
return;
}
if ((wav_reader->channels() != 1) || (wav_reader->bits_per_sample() != 16)) {
nav_.display_modal("Error", "Wrong format.\nWav viewer only accepts\n16-bit mono files.", INFO, nullptr);
return;
}
load_wav(file_path);
field_pos_seconds.focus();
};
@@ -176,7 +175,6 @@ ViewWavView::ViewWavView(
refresh_measurements();
};
reset_controls();
}
void ViewWavView::focus() {
+179 -72
View File
@@ -30,83 +30,190 @@
using namespace portapack;
namespace ui {
namespace ui
{
void WhipCalcView::focus() {
field_frequency.focus();
}
void WhipCalcView::update_result() {
double length, divider;
divider = ((double)options_type.selected_index_value() / 8.0);
// Metric
length = (speed_of_light_mps / (double)field_frequency.value()) * divider;
auto m = to_string_dec_int((int)length, 2);
auto cm = to_string_dec_int(int(length * 100.0) % 100, 2);
auto mm = to_string_dec_int(int(length * 1000.0) % 10, 1);
text_result_metric.set(m + "m " + cm + "." + mm + "cm");
// ANT500 elements for crude adjustment
length /= 0.14;
if (int(length) <= 4) {
auto elements = to_string_dec_int((int)length, 1);
text_result_ant500.set(elements + " " + frac_str[((int(length * 10.0) % 10) + 1) / 3] + "ANT500 elements");
} else {
text_result_ant500.set("-");
void WhipCalcView::focus()
{
field_frequency.focus();
}
// Imperial
length = (speed_of_light_fps / (double)field_frequency.value()) * divider;
auto feet = to_string_dec_int((int)length, 3);
auto inch = to_string_dec_int(int(length * 10.0) % 12, 2);
auto inch_c = to_string_dec_int(int(length * 100.0) % 10, 1);
text_result_imperial.set(feet + "ft " + inch + "." + inch_c + "in");
}
void WhipCalcView::update_result()
{
double length, calclength, divider;
console.clear(true);
divider = ((double)options_type.selected_index_value() / 8.0);
WhipCalcView::WhipCalcView(
NavigationView& nav
) {
// Antenna lengths fields
if (field_frequency.value() > 0)
{
// Metric
length = (speed_of_light_mps / (double)field_frequency.value()) * divider;
auto m = to_string_dec_int((int)length, 0);
//auto cm = to_string_dec_int(int(length * 100.0) % 100, 2);
//auto mm = to_string_dec_int(int(length * 1000.0) % 10, 1);
calclength = get_decimals(length, 100); //cm
auto cm = to_string_dec_int(int(calclength), 0);
auto mm = to_string_dec_int(int(get_decimals(calclength, 10, true)), 0);
text_result_metric.set(m + "m " + cm + "." + mm + "cm");
add_children({
&labels,
&field_frequency,
&options_type,
&text_result_metric,
&text_result_imperial,
&text_result_ant500,
&button_exit
});
options_type.on_change = [this](size_t, OptionsField::value_t) {
this->update_result();
};
options_type.set_selected_index(2); // Quarter wave
field_frequency.set_value(transmitter_model.tuning_frequency());
field_frequency.set_step(500000); // 500kHz step
field_frequency.on_change = [this](rf::Frequency) {
this->update_result();
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.tuning_frequency());
new_view->on_changed = [this](rf::Frequency f) {
// Imperial
calclength = (speed_of_light_fps / (double)field_frequency.value()) * divider;
auto feet = to_string_dec_int(int(calclength), 0);
calclength = get_decimals(calclength, 12); //inches
auto inch = to_string_dec_int(int(calclength), 0);
auto inch_c = to_string_dec_int(int(get_decimals(calclength, 10, true)), 0);
text_result_imperial.set(feet + "ft " + inch + "." + inch_c + "in");
}
else
{
text_result_metric.set("infinity+");
text_result_imperial.set("infinity+");
return;
}
uint8_t ant_count = 9; //Shown antennas counter
length *= 1000; //Get length in mm needed to extend the antenna
for (antenna_entry antenna : antenna_db)
{ //go thru all antennas available
uint16_t element, refined_quarter = 0;
for (element = 0; element < antenna.elements.size(); element++)
{
if (length == antenna.elements[element]) //Exact element in length
{
element++; //Real element is +1 (zero based vector)
break; //Done with this ant
}
else if (length < antenna.elements[element])
{
double remain, this_element, quarter = 0;
remain = length - antenna.elements[element - 1]; //mm needed from this element to reach length
this_element = antenna.elements[element] - antenna.elements[element - 1]; //total mm on this element
quarter = (remain * 4) / this_element; //havoc & portack ended on this int(quarter) resolution.
if (quarter - int(quarter) > 0.5)
{ //rounding gave a measure closer to next quarter
refined_quarter = int(quarter) + 1;
if (refined_quarter == 4)
{ //rounding gave a measure closer to next element
refined_quarter = 0;
element++;
}
}
else
{
refined_quarter = int(quarter);
}
break; //Done with this ant
}
}
/*if (!ant_count)
{
console.write(" and more ...");
break;
}*/
console.write(antenna.label + ": " + to_string_dec_int(element, 1) + frac_str[refined_quarter] + " elements\n");
ant_count--; // For now, just showing all.
}
}
WhipCalcView::WhipCalcView(NavigationView &nav)
{
add_children({&labels,
//&antennas_on_memory,
&field_frequency,
&options_type,
&text_result_metric,
&text_result_imperial,
&console,
&button_exit});
File antennas_file;
auto error = antennas_file.open("WHIPCALC/ANTENNAS.TXT");
if (!error.is_valid())
{
std::string line;
char one_char[1];
for (size_t pointer = 0; pointer < antennas_file.size(); pointer++)
{
antennas_file.seek(pointer);
antennas_file.read(one_char, 1);
if ((int)one_char[0] >= ' ')
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 (!antenna_db.size())
add_default_antenna();
//antennas_on_memory.set(to_string_dec_int(antenna_db.size(),0) + " antennas"); //tell user
options_type.set_selected_index(2); // Quarter wave
options_type.on_change = [this](size_t, OptionsField::value_t) {
this->update_result();
this->field_frequency.set_value(f);
};
};
button_exit.on_select = [this, &nav](Button&) {
nav.pop();
};
update_result();
}
}
field_frequency.set_step(1000000); // 1MHz step
field_frequency.on_change = [this](rf::Frequency) {
this->update_result();
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.tuning_frequency());
new_view->on_changed = [this](rf::Frequency f) {
this->field_frequency.set_value(f);
this->update_result();
};
};
button_exit.on_select = [this, &nav](Button &) {
nav.pop();
};
field_frequency.set_value(transmitter_model.tuning_frequency());
}
void ui::WhipCalcView::txtline_process(std::string &line)
{
if (line.find("#") != std::string::npos)
return; //Line is just a comment
char separator = ',';
size_t previous = 0;
uint16_t value = 0;
antenna_entry new_antenna;
size_t current = line.find(separator);
while (current != std::string::npos)
{
if (!previous)
new_antenna.label.assign(line, 0, current); //antenna label
else
{
value = std::stoi(line.substr(previous, current - previous));
if (!value) return; //No element length? abort antenna
new_antenna.elements.push_back(value); //Store this new element
}
previous = current + 1;
current = line.find(separator, previous); //Search for next space delimiter
}
if (!previous) return; //Not even a label ? drop this antenna!
value = std::stoi(line.substr(previous, current - previous)); //Last element
if (!value) return;
new_antenna.elements.push_back(value);
antenna_db.push_back(new_antenna); //Add this antenna
}
void ui::WhipCalcView::add_default_antenna()
{
antenna_db.push_back({"ANT500", {185, 315, 450, 586, 724, 862}}); //store a default ant500
}
} // namespace ui
+59 -57
View File
@@ -28,68 +28,70 @@
#include "ui_receiver.hpp"
#include "ui_navigation.hpp"
#include "string_format.hpp"
#include <vector>
namespace ui {
namespace ui
{
class WhipCalcView : public View
{
public:
WhipCalcView(NavigationView &nav);
void focus() override;
std::string title() const override { return "Antenna length"; };
class WhipCalcView : public View {
public:
WhipCalcView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Whip calculator"; };
private:
const double speed_of_light_mps = 299792458.0; // m/s
const double speed_of_light_fps = 983571087.90472; // feet/s
const std::string frac_str[4] = {"", " 1/4", " 1/2", " 3/4"};
private:
const double speed_of_light_mps = 299792458.0; // m/s
const double speed_of_light_fps = 983571087.90472; // feet/s
const std::string frac_str[4] = { "", "1/4 ", "1/2 ", "3/4 " };
void update_result();
Labels labels {
{ { 2 * 8, 2 * 16 }, "Frequency:", Color::light_grey() },
{ { 2 * 8, 3 * 16 }, "Type:", Color::light_grey() }
};
FrequencyField field_frequency {
{ 13 * 8, 2 * 16 },
};
OptionsField options_type {
{ 8 * 8, 3 * 16 },
12,
struct antenna_entry
{
{ "Full wave", 8 },
{ "Half wave", 4 },
{ "Quarter wave", 2 },
{ "3/4 wave", 6 },
{ "1/8 wave", 1 },
{ "3/8 wave", 3 },
{ "5/8 wave", 5 },
{ "7/8 wave", 7 }
}
std::string label{};
std::vector<uint16_t> elements{};
};
std::vector<antenna_entry> antenna_db{};
void update_result();
uint16_t string_to_number(std::string);
void txtline_process(std::string &);
void add_default_antenna();
Labels labels{
{{2 * 8, 1 * 16}, "Frequency:", Color::light_grey()},
{{7 * 8, 2 * 16}, "Wave:", Color::light_grey()},
{{5 * 8, 3 * 16}, "Metric:", Color::light_grey()},
{{3 * 8, 4 * 16}, "Imperial:", Color::light_grey()}};
FrequencyField field_frequency{
{13 * 8, 1 * 16},
};
OptionsField options_type{
{13 * 8, 2 * 16},
7,
{{"Full", 8},
{"Half", 4},
{"Quarter", 2},
{"3/4", 6},
{"1/8", 1},
{"3/8", 3},
{"5/8", 5},
{"7/8", 7}}};
Text text_result_metric{
{13 * 8, 3 * 16, 10 * 16, 16},
"-"};
Text text_result_imperial{
{13 * 8, 4 * 16, 10 * 16, 16},
"-"};
Console console{
{0, 6 * 16, 240, 160}};
Button button_exit{
{72, 17 * 16, 96, 32},
"Back"};
};
Text text_result_metric {
{ 3 * 8, 5 * 16, 10 * 16, 16 },
"-"
};
Text text_result_imperial {
{ 2 * 8, 6 * 16, 10 * 16, 16 },
"-"
};
Text text_result_ant500 {
{ 2 * 8, 8 * 16, 26 * 16, 16 },
"-"
};
Button button_exit {
{ 72, 264, 96, 32 },
"Exit"
};
};
} /* namespace ui */
#endif/*__UI_WHIPCALC__*/
#endif /*__UI_WHIPCALC__*/
+1 -1
View File
@@ -39,7 +39,7 @@ static void send_message(const Message* const message) {
// If message is only sent by this function via one thread, no need to check if
// another message is present before setting new message.
shared_memory.baseband_message = message;
creg::m0apptxevent::assert();
creg::m0apptxevent::assert_event();
while(shared_memory.baseband_message);
}
+44
View File
@@ -889,6 +889,28 @@ static constexpr Bitmap bitmap_icon_load {
{ 16, 16 }, bitmap_icon_load_data
};
static constexpr uint8_t bitmap_icon_looking_data[] = {
0xF8, 0x01,
0xFC, 0x03,
0x0E, 0x07,
0x07, 0x0E,
0xF3, 0x0C,
0x9F, 0x0F,
0x9F, 0x0F,
0xF3, 0x0C,
0x07, 0x0E,
0x0E, 0x07,
0xFC, 0x1F,
0xF8, 0x3D,
0x00, 0x7C,
0x00, 0xF8,
0x00, 0xF0,
0x00, 0x60,
};
static constexpr Bitmap bitmap_icon_looking {
{ 16, 16 }, bitmap_icon_looking_data
};
static constexpr uint8_t bitmap_icon_lora_data[] = {
0xC0, 0x03,
0x30, 0x0C,
@@ -2433,6 +2455,28 @@ static constexpr Bitmap bitmap_target_verify {
{ 32, 32 }, bitmap_target_verify_data
};
static constexpr uint8_t bitmap_titlebar_image_data[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x22, 0xC7, 0x8B, 0x9E, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00,
0xA2, 0x28, 0x48, 0xA6, 0x81, 0x31, 0x67, 0x36, 0x73, 0x63,
0xBE, 0x2F, 0x38, 0x9E, 0x87, 0xBB, 0x6D, 0x36, 0x1B, 0x77,
0xA2, 0x28, 0x48, 0xA6, 0x81, 0xBF, 0x6D, 0x36, 0x1B, 0x7F,
0xA2, 0xC8, 0x8B, 0xA6, 0x81, 0xB5, 0xCF, 0xF3, 0x7B, 0x6B,
0x00, 0x00, 0x00, 0x00, 0x80, 0xB1, 0x8D, 0x31, 0x1B, 0x63,
0xEE, 0xEE, 0xEE, 0xEE, 0x8A, 0xB1, 0x8D, 0x31, 0x1B, 0x63,
0xAE, 0x46, 0xEE, 0x2E, 0x86, 0xB1, 0x8D, 0x31, 0x73, 0x63,
0xE2, 0x4A, 0x2A, 0xEA, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static constexpr Bitmap bitmap_titlebar_image {
{ 80, 16 }, bitmap_titlebar_image_data
};
} /* namespace ui */
+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;
+5 -1
View File
@@ -29,7 +29,11 @@ std::vector<std::string> get_freqman_files() {
auto files = scan_root_files(u"FREQMAN", u"*.TXT");
for (auto file : files) {
file_list.emplace_back(file.stem().string());
std::string file_name = file.stem().string();
// don't propose tmp / hidden files in freqman's list
if (file_name.length() && file_name[0] != '.') {
file_list.emplace_back(file_name);
}
}
return file_list;
+17
View File
@@ -48,11 +48,28 @@ enum freqman_entry_type {
RANGE
};
//Entry step placed for AlainD freqman version (or any other enhanced version)
enum freqman_entry_step {
STEP_DEF = 0, // default
AM_US, // 10 Khz AM/CB
AM_EUR, // 9 Khz LW/MW
NFM_1, // 12,5 Khz (Analogic PMR 446)
NFM_2, // 6,25 Khz (Digital PMR 446)
FM_1, // 100 Khz
FM_2, // 50 Khz
N_1, // 25 Khz
N_2, // 250 Khz
AIRBAND, // AIRBAND 8,33 Khz
ERROR_STEP
};
// freqman_entry_step step added, as above, to provide compatibility / future enhancement.
struct freqman_entry {
rf::Frequency frequency_a { 0 };
rf::Frequency frequency_b { 0 };
std::string description { };
freqman_entry_type type { };
freqman_entry_step step { };
};
using freqman_db = std::vector<freqman_entry>;
+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() {
+2 -1
View File
@@ -38,10 +38,11 @@ using namespace portapack;
namespace {
static constexpr std::array<baseband::AMConfig, 3> am_configs { {
static constexpr std::array<baseband::AMConfig, 4> am_configs { {
{ taps_6k0_dsb_channel, AMConfigureMessage::Modulation::DSB },
{ taps_2k8_usb_channel, AMConfigureMessage::Modulation::SSB },
{ taps_2k8_lsb_channel, AMConfigureMessage::Modulation::SSB },
{ taps_0k7_usb_channel, AMConfigureMessage::Modulation::SSB },
} };
static constexpr std::array<baseband::NBFMConfig, 3> nbfm_configs { {
+12 -2
View File
@@ -113,7 +113,7 @@ std::string to_string_dec_int(
}
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');
auto final_str = to_string_dec_int(f / 1000000,4) + "." + to_string_dec_int((f / 100) % 10000, 4, '0');
return final_str;
}
@@ -171,7 +171,7 @@ std::string to_string_datetime(const rtc::RTC& value, const TimeFormat format) {
}
string += to_string_dec_uint(value.hour(), 2, '0') + ":" +
string += to_string_dec_uint(value.minute(), 2, '0');
to_string_dec_uint(value.minute(), 2, '0');
if ((format == YMDHMS) || (format == HMS))
string += ":" + to_string_dec_uint(value.second(), 2, '0');
@@ -223,3 +223,13 @@ std::string unit_auto_scale(double n, const uint32_t base_nano, uint32_t precisi
return string;
}
double get_decimals(double num, int16_t mult, bool round) {
num -= int(num); //keep decimals only
num *= mult; //Shift decimals into integers
if (!round) return num;
int16_t intnum = int(num); //Round it up if necessary
num -= intnum; //Get decimal part
if (num > .5) intnum++; //Round up
return intnum;
}
+1 -1
View File
@@ -54,5 +54,5 @@ std::string to_string_timestamp(const rtc::RTC& value);
std::string to_string_FAT_timestamp(const FATTimestamp& timestamp);
std::string unit_auto_scale(double n, const uint32_t base_nano, uint32_t precision);
double get_decimals(double num, int16_t mult, bool round = false); //euquiq added
#endif/*__STRING_FORMAT_H__*/
+9
View File
@@ -95,6 +95,15 @@ void BtnGridView::set_parent_rect(const Rect new_parent_rect) {
update_items();
}
void BtnGridView::set_arrow_enabled(bool new_value) {
if(new_value){
add_child(&arrow_more);
}
else{
remove_child(&arrow_more);
}
};
void BtnGridView::on_tick_second() {
if (more && blink)
arrow_more.set_foreground(Color::white());
+1
View File
@@ -62,6 +62,7 @@ public:
uint32_t highlighted_index();
void set_parent_rect(const Rect new_parent_rect) override;
void set_arrow_enabled(bool new_value);
void on_focus() override;
void on_blur() override;
bool on_key(const KeyEvent event) override;
+19 -9
View File
@@ -59,7 +59,7 @@ GeoPos::GeoPos(
set_altitude(0);
set_lat(0);
set_lon(0);
const auto changed_fn = [this](int32_t) {
float lat_value = lat();
float lon_value = lon();
@@ -163,14 +163,22 @@ void GeoMap::paint(Painter& painter) {
prev_x_pos = x_pos;
prev_y_pos = y_pos;
}
//center tag above point
if(tag_.find_first_not_of(' ') != tag_.npos){ //only draw tag if we have something other than spaces
painter.draw_string(r.center() - Point(((int)tag_.length() * 8 / 2), 2 * 16), style(), tag_);
}
if (mode_ == PROMPT) {
// Cross
display.fill_rectangle({ r.center() - Point(16, 1), { 32, 2 } }, Color::red());
display.fill_rectangle({ r.center() - Point(1, 16), { 2, 32 } }, Color::red());
} else {
draw_bearing({ 120, 32 + 144 }, angle_, 10, Color::red());
painter.draw_string({ 120 - ((int)tag_.length() * 8 / 2), 32 + 144 - 32 }, style(), tag_);
} else if (angle_ < 360){
//if we have a valid angle draw bearing
draw_bearing(r.center(), angle_, 10, Color::red());
}
else {
//draw a small cross
display.fill_rectangle({ r.center() - Point(8, 1), { 16, 2 } }, Color::red());
display.fill_rectangle({ r.center() - Point(1, 8), { 2, 16 } }, Color::red());
}
}
@@ -230,7 +238,7 @@ void GeoMap::set_mode(GeoMapMode mode) {
mode_ = mode;
}
void GeoMap::draw_bearing(const Point origin, const uint32_t angle, uint32_t size, const Color color) {
void GeoMap::draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color) {
Point arrow_a, arrow_b, arrow_c;
for (size_t thickness = 0; thickness < 3; thickness++) {
@@ -253,11 +261,12 @@ void GeoMapView::focus() {
nav_.display_modal("No map", "No world_map.bin file in\n/ADSB/ directory", ABORT, nullptr);
}
void GeoMapView::update_position(float lat, float lon) {
void GeoMapView::update_position(float lat, float lon, uint16_t angle) {
lat_ = lat;
lon_ = lon;
geopos.set_lat(lat_);
geopos.set_lon(lon_);
geomap.set_angle(angle);
geomap.move(lon_, lat_);
geomap.set_dirty();
}
@@ -268,7 +277,7 @@ void GeoMapView::setup() {
geopos.set_altitude(altitude_);
geopos.set_lat(lat_);
geopos.set_lon(lon_);
geopos.on_change = [this](int32_t altitude, float lat, float lon) {
altitude_ = altitude;
lat_ = lat;
@@ -306,7 +315,7 @@ GeoMapView::GeoMapView(
GeoPos::alt_unit altitude_unit,
float lat,
float lon,
float angle,
uint16_t angle,
const std::function<void(void)> on_close
) : nav_ (nav),
altitude_ (altitude),
@@ -327,6 +336,7 @@ GeoMapView::GeoMapView(
geomap.set_mode(mode_);
geomap.set_tag(tag);
geomap.set_angle(angle);
geomap.move(lon_, lat_);
geopos.set_read_only(true);
+9 -5
View File
@@ -129,8 +129,12 @@ public:
tag_ = new_tag;
}
void set_angle(uint16_t new_angle){
angle_ = new_angle;
}
private:
void draw_bearing(const Point origin, const uint32_t angle, uint32_t size, const Color color);
void draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color);
GeoMapMode mode_ { };
File map_file { };
@@ -141,7 +145,7 @@ private:
int32_t prev_x_pos { 0xFFFF }, prev_y_pos { 0xFFFF };
float lat_ { };
float lon_ { };
float angle_ { };
uint16_t angle_ { };
std::string tag_ { };
};
@@ -154,7 +158,7 @@ public:
GeoPos::alt_unit altitude_unit,
float lat,
float lon,
float angle,
uint16_t angle,
const std::function<void(void)> on_close = nullptr
);
GeoMapView(NavigationView& nav,
@@ -173,7 +177,7 @@ public:
void focus() override;
void update_position(float lat, float lon);
void update_position(float lat, float lon, uint16_t angle);
std::string title() const override { return "Map view"; };
@@ -190,7 +194,7 @@ private:
GeoPos::alt_unit altitude_unit_ { };
float lat_ { };
float lon_ { };
float angle_ { };
uint16_t angle_ { };
std::function<void(void)> on_close_ { nullptr };
bool map_opened { };
+25 -34
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();
}
}
@@ -120,8 +123,8 @@ void FrequencyScale::paint(Painter& painter) {
if (_blink) {
const Rect r_cursor {
120 + cursor_position, r.bottom() - filter_band_height,
2, filter_band_height
118 + cursor_position, r.bottom() - filter_band_height,
5, filter_band_height
};
painter.fill_rectangle(
r_cursor,
@@ -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);
+120 -21
View File
@@ -30,7 +30,7 @@
#include "bmp_modal_warning.hpp"
#include "portapack_persistent_memory.hpp"
#include "ui_about.hpp"
#include "ui_about_simple.hpp"
#include "ui_adsb_rx.hpp"
#include "ui_adsb_tx.hpp"
#include "ui_afsk_rx.hpp"
@@ -81,7 +81,7 @@
#include "tpms_app.hpp"
#include "core_control.hpp"
#include "ui_looking_glass_app.hpp"
#include "file.hpp"
#include "png_writer.hpp"
@@ -106,13 +106,14 @@ SystemStatusView::SystemStatusView(
&backdrop,
&button_back,
&title,
&button_title,
&button_speaker,
&button_stealth,
//&button_textentry,
&button_camera,
&button_sleep,
&button_bias_tee,
&image_clock_status,
&button_clock_status,
&sd_card_status_view,
});
@@ -138,6 +139,10 @@ SystemStatusView::SystemStatusView(
if (this->on_back)
this->on_back();
};
button_title.on_select = [this](ImageButton&) {
this->on_title();
};
button_speaker.on_select = [this](ImageButton&) {
this->on_speaker();
@@ -163,6 +168,10 @@ SystemStatusView::SystemStatusView(
DisplaySleepMessage message;
EventDispatcher::send_message(message);
};
button_clock_status.on_select = [this](ImageButton&) {
this->on_clk();
};
}
void SystemStatusView::refresh() {
@@ -183,19 +192,39 @@ 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();
}
void SystemStatusView::set_back_enabled(bool new_value) {
button_back.set_foreground(new_value ? Color::white() : Color::dark_grey());
button_back.set_focusable(new_value);
if(new_value){
add_child(&button_back);
}
else{
remove_child(&button_back);
}
}
void SystemStatusView::set_title_image_enabled(bool new_value) {
if(new_value){
add_child(&button_title);
}
else{
remove_child(&button_title);
}
}
void SystemStatusView::set_title(const std::string new_value) {
@@ -282,6 +311,51 @@ 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>();
else
nav_.pop();
}
/* Information View *****************************************************/
InformationView::InformationView(
NavigationView& nav
) : nav_ (nav)
{
static constexpr Style style_infobar {
.font = font::fixed_8x16,
.background = {33, 33, 33},
.foreground = Color::white(),
};
add_children({
&backdrop,
&version,
&ltime
});
version.set_style(&style_infobar);
ltime.set_style(&style_infobar);
ltime.set_seconds_enabled(true);
ltime.set_date_enabled(false);
set_dirty();
}
/* Navigation ************************************************************/
bool NavigationView::is_top() const {
@@ -383,7 +457,7 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) {
add_items({
//{ "..", ui::Color::light_grey(),&bitmap_icon_previous, [&nav](){ nav.pop(); } },
{ "ADS-B", ui::Color::green(), &bitmap_icon_adsb, [&nav](){ nav.push<ADSBRxView>(); }, },
{ "ACARS", ui::Color::yellow(), &bitmap_icon_adsb, [&nav](){ nav.push<ACARSAppView>(); }, },
//{ "ACARS", ui::Color::yellow(), &bitmap_icon_adsb, [&nav](){ nav.push<ACARSAppView>(); }, },
{ "AIS Boats", ui::Color::green(), &bitmap_icon_ais, [&nav](){ nav.push<AISAppView>(); } },
{ "AFSK", ui::Color::yellow(), &bitmap_icon_modem, [&nav](){ nav.push<AFSKRxView>(); } },
{ "BTLE", ui::Color::yellow(), &bitmap_icon_btle, [&nav](){ nav.push<BTLERxView>(); } },
@@ -392,7 +466,7 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) {
{ "Analog TV", ui::Color::yellow(), &bitmap_icon_sstv, [&nav](){ nav.push<AnalogTvView>(); } },
{ "ERT Meter", ui::Color::green(), &bitmap_icon_ert, [&nav](){ nav.push<ERTAppView>(); } },
{ "POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav](){ nav.push<POCSAGAppView>(); } },
{ "Radiosnde", ui::Color::yellow(), &bitmap_icon_sonde, [&nav](){ nav.push<SondeView>(); } },
{ "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>(); } },
{ "DMR", ui::Color::dark_grey(), &bitmap_icon_dmr, [&nav](){ nav.push<NotImplementedView>(); } },
@@ -411,13 +485,12 @@ 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>(); } },
{ "Mic", ui::Color::green(), &bitmap_icon_microphone,[&nav](){ nav.push<MicTXView>(); } },
{ "Morse", ui::Color::green(), &bitmap_icon_morse, [&nav](){ nav.push<MorseView>(); } },
{ "BurgerPgr", ui::Color::yellow(), &bitmap_icon_burger, [&nav](){ nav.push<CoasterPagerView>(); } },
//{ "Nuoptix DTMF", ui::Color::green(), &bitmap_icon_nuoptix, [&nav](){ nav.push<NuoptixView>(); } },
@@ -443,8 +516,8 @@ UtilitiesMenuView::UtilitiesMenuView(NavigationView& nav) {
//{ "Notepad", ui::Color::dark_grey(), &bitmap_icon_notepad, [&nav](){ nav.push<NotImplementedView>(); } },
{ "Signal gen", ui::Color::green(), &bitmap_icon_cwgen, [&nav](){ nav.push<SigGenView>(); } },
//{ "Tone search", ui::Color::dark_grey(), nullptr, [&nav](){ nav.push<ToneSearchView>(); } },
{ "Wave viewer", ui::Color::yellow(), &bitmap_icon_soundboard, [&nav](){ nav.push<ViewWavView>(); } },
{ "Antenna length", ui::Color::green(), &bitmap_icon_tools_antenna, [&nav](){ nav.push<WhipCalcView>(); } },
{ "Wave viewer", ui::Color::yellow(), &bitmap_icon_soundboard, [&nav](){ nav.push<ViewWavView>(); } },
{ "Antenna length", ui::Color::green(), &bitmap_icon_tools_antenna, [&nav](){ nav.push<WhipCalcView>(); } },
{ "Wipe SD card", ui::Color::red(), &bitmap_icon_tools_wipesd, [&nav](){ nav.push<WipeSDView>(); } },
});
set_max_rows(2); // allow wider buttons
@@ -465,19 +538,22 @@ void SystemMenuView::hackrf_mode(NavigationView& nav) {
SystemMenuView::SystemMenuView(NavigationView& nav) {
add_items({
//{ "Play dead", ui::Color::red(), &bitmap_icon_playdead, [&nav](){ nav.push<PlayDeadView>(); } },
{ "Receive", ui::Color::cyan(), &bitmap_icon_receivers, [&nav](){ nav.push<ReceiversMenuView>(); } },
{ "Receive", ui::Color::cyan(), &bitmap_icon_receivers, [&nav](){ nav.push<ReceiversMenuView>(); } },
{ "Transmit", ui::Color::cyan(), &bitmap_icon_transmit, [&nav](){ nav.push<TransmittersMenuView>(); } },
{ "Capture", ui::Color::red(), &bitmap_icon_capture, [&nav](){ nav.push<CaptureAppView>(); } },
{ "Replay", ui::Color::green(), &bitmap_icon_replay, [&nav](){ nav.push<ReplayAppView>(); } },
{ "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::yellow(), &bitmap_icon_scanner, [&nav](){ nav.push<ScannerView>(); } },
{ "Tools", ui::Color::cyan(), &bitmap_icon_utilities, [&nav](){ nav.push<UtilitiesMenuView>(); } },
{ "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>(); } },
{ "Debug", ui::Color::light_grey(), &bitmap_icon_debug, [&nav](){ nav.push<DebugMenuView>(); } },
{ "HackRF", ui::Color::cyan(), &bitmap_icon_hackrf, [this, &nav](){ hackrf_mode(nav); } },
//{ "About", ui::Color::cyan(), nullptr, [&nav](){ nav.push<AboutView>(); } }
});
set_max_rows(2); // allow wider buttons
set_arrow_enabled(false);
//set_highlighted(1); // Startup selection
}
@@ -498,6 +574,7 @@ SystemView::SystemView(
set_style(&style_default);
constexpr ui::Dim status_view_height = 16;
constexpr ui::Dim info_view_height = 16;
add_child(&status_view);
status_view.set_parent_rect({
@@ -513,11 +590,30 @@ SystemView::SystemView(
{ 0, status_view_height },
{ parent_rect.width(), static_cast<ui::Dim>(parent_rect.height() - status_view_height) }
});
add_child(&info_view);
info_view.set_parent_rect({
{0, 19 * 16},
{ parent_rect.width(), info_view_height }
});
navigation_view.on_view_changed = [this](const View& new_view) {
if(!this->navigation_view.is_top()){
remove_child(&info_view);
}
else{
add_child(&info_view);
}
this->status_view.set_back_enabled(!this->navigation_view.is_top());
this->status_view.set_title_image_enabled(this->navigation_view.is_top());
this->status_view.set_title(new_view.title());
this->status_view.set_dirty();
};
// portapack::persistent_memory::set_playdead_sequence(0x8D1);
// Initial view
@@ -530,6 +626,9 @@ SystemView::SystemView(
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();
//else
// navigation_view.push<SystemMenuView>();
+36 -2
View File
@@ -106,10 +106,11 @@ public:
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 = "MAYHEM v1.0.3"; // TODO: Move the version somewhere
static constexpr auto default_title = "";
NavigationView& nav_;
@@ -130,6 +131,13 @@ private:
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,
@@ -172,7 +180,7 @@ private:
Color::dark_grey()
};
Image image_clock_status {
ImageButton button_clock_status {
{ 27 * 8, 0 * 16, 2 * 8, 1 * 16 },
&bitmap_icon_clk_int,
Color::light_grey(),
@@ -188,7 +196,9 @@ private:
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,
@@ -199,6 +209,29 @@ private:
};
};
class InformationView : public View {
public:
InformationView(NavigationView& nav);
private:
static constexpr auto version_string = "v1.3.0";
NavigationView& nav_;
Rectangle backdrop {
{ 0, 0 * 16, 240, 16 },
{33, 33, 33}
};
Text version {
{2, 0, 11 * 8, 16},
version_string
};
LiveDateTime ltime {
{174, 0, 8 * 8, 16}
};
};
class BMPView : public View {
public:
BMPView(NavigationView& nav);
@@ -253,6 +286,7 @@ public:
private:
SystemStatusView status_view { navigation_view };
InformationView info_view { navigation_view };
NavigationView navigation_view { };
Context& context_;
};
+13 -1
View File
@@ -46,6 +46,18 @@ public:
Incomplete = 0,
OK = 1,
};
std::string ResultStr[10] = {
"Compare",
"Read incomplete",
"Write incomplete",
"Abort",
"File Open Read",
"File Open Write",
"Heap",
"Thread",
"Incomplete",
"OK",
};
struct Stats {
halrtcnt_t write_duration_min { 0 };
@@ -423,7 +435,7 @@ void SDCardDebugView::on_test() {
format_bytes_per_ticks_as_mib(stats.read_bytes, stats.read_test_duration)
);
} else {
text_test_write_time_value.set("Fail: " + to_string_dec_int(toUType(thread.result()), 4));
text_test_write_time_value.set("Fail: " + thread.ResultStr[thread.result() + 8]);
}
}
+7 -4
View File
@@ -34,12 +34,14 @@ 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_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);
// TODO: Feed channel_stats post-decimation data?
feed_channel_stats(channel_out);
channel_spectrum.feed(channel_out, channel_filter_pass_f, channel_filter_stop_f);
auto audio = demodulate(channel_out);
audio_compressor.execute_in_place(audio);
@@ -91,9 +93,10 @@ void NarrowbandAMAudio::configure(const AMConfigureMessage& message) {
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_spectrum.set_decimation_factor(std::floor(channel_filter_output_fs / (channel_filter_pass_f + channel_filter_stop_f)));
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 { };
+4 -3
View File
@@ -52,7 +52,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 +85,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 { };
+2 -1
View File
@@ -51,7 +51,8 @@ private:
ToneGen tone_gen { };
ToneGen beep_gen { };
uint32_t divider { }, audio_gain { };
uint32_t divider { };
float audio_gain { };
uint64_t power_acc { 0 };
uint32_t power_acc_count { 0 };
bool play_beep { false };
+7 -4
View File
@@ -38,10 +38,12 @@ 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_low_f, channel_filter_high_f, channel_filter_transition);
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer);
feed_channel_stats(channel_out);
channel_spectrum.feed(channel_out, channel_filter_pass_f, channel_filter_stop_f);
if (!pitch_rssi_enabled) {
// Normal mode, output demodulated audio
@@ -143,9 +145,10 @@ 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_spectrum.set_decimation_factor(std::floor(channel_filter_output_fs / (channel_filter_pass_f + channel_filter_stop_f)));
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);
hpf.configure(audio_24k_hpf_30hz_config);
+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 { };
+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 { };
+2 -1
View File
@@ -33,6 +33,7 @@ void SigGenProcessor::execute(const buffer_c8_t& buffer) {
for (size_t i = 0; i < buffer.count; i++) {
if (!sample_count && auto_off) {
configured = false;
txprogress_message.done = true;
shared_memory.application_queue.push(txprogress_message);
} else
@@ -40,7 +41,7 @@ void SigGenProcessor::execute(const buffer_c8_t& buffer) {
if (tone_shape == 0) {
// CW
re = 0;
re = 127;
im = 0;
} else {
+2 -1
View File
@@ -140,7 +140,8 @@ private:
}
};
PacketBuilder<BitPattern, NeverMatch, FixedLength> packet_builder_fsk_4800_Vaisala {
{ 0b00001000011011010101001110001000, 32, 1 },
{ 0b00001000011011010101001110001000, 32, 1 }, //euquiq Header detects 4 of 8 bytes 0x10B6CA11 /this is in raw format) (these bits are not passed at the beginning of packet)
//{ 0b0000100001101101010100111000100001000100011010010100100000011111, 64, 1 }, //euquiq whole header detection would be 8 bytes.
{ },
{ 320 * 8 },
[this](const baseband::Packet& packet) {
+4 -3
View File
@@ -43,7 +43,7 @@ void WidebandFMAudio::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);
}
/* 384kHz complex<int16_t>[256]
@@ -169,8 +169,9 @@ void WidebandFMAudio::configure(const WFMConfigureMessage& message) {
decim_0.configure(message.decim_0_filter.taps, 33554432);
decim_1.configure(message.decim_1_filter.taps, 131072);
channel_filter_pass_f = message.decim_1_filter.pass_frequency_normalized * decim_1_input_fs;
channel_filter_stop_f = message.decim_1_filter.stop_frequency_normalized * decim_1_input_fs;
channel_filter_low_f = message.decim_1_filter.low_frequency_normalized * decim_1_input_fs;
channel_filter_high_f = message.decim_1_filter.high_frequency_normalized * decim_1_input_fs;
channel_filter_transition = message.decim_1_filter.transition_normalized * decim_1_input_fs;
demod.configure(demod_input_fs, message.deviation);
audio_filter.configure(message.audio_filter.taps);
audio_output.configure(message.audio_hpf_config, message.audio_deemph_config);
+3 -2
View File
@@ -67,8 +67,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;
dsp::demodulate::FM demod { };
dsp::decimate::DecimateBy2CIC4Real audio_dec_1 { };
+1 -1
View File
@@ -53,7 +53,7 @@ void WidebandSpectrum::execute(const buffer_c8_t& buffer) {
};
channel_spectrum.feed(
buffer_c16,
0, 0
0, 0, 0
);
phase = 0;
} else {
+9 -6
View File
@@ -78,12 +78,14 @@ void SpectrumCollector::set_decimation_factor(
void SpectrumCollector::feed(
const buffer_c16_t& channel,
const uint32_t filter_pass_frequency,
const uint32_t filter_stop_frequency
const int32_t filter_low_frequency,
const int32_t filter_high_frequency,
const int32_t filter_transition
) {
// Called from baseband processing thread.
channel_filter_pass_frequency = filter_pass_frequency;
channel_filter_stop_frequency = filter_stop_frequency;
channel_filter_low_frequency = filter_low_frequency;
channel_filter_high_frequency = filter_high_frequency;
channel_filter_transition = filter_transition;
channel_spectrum_decimator.feed(
channel,
@@ -136,8 +138,9 @@ void SpectrumCollector::update() {
ChannelSpectrum spectrum;
spectrum.sampling_rate = channel_spectrum_sampling_rate;
spectrum.channel_filter_pass_frequency = channel_filter_pass_frequency;
spectrum.channel_filter_stop_frequency = channel_filter_stop_frequency;
spectrum.channel_filter_low_frequency = channel_filter_low_frequency;
spectrum.channel_filter_high_frequency = channel_filter_high_frequency;
spectrum.channel_filter_transition = channel_filter_transition;
for(size_t i=0; i<spectrum.db.size(); i++) {
const auto corrected_sample = spectrum_window_hamming_3(channel_spectrum, i);
const auto mag2 = magnitude_squared(corrected_sample * (1.0f / 32768.0f));
+6 -4
View File
@@ -40,8 +40,9 @@ public:
void feed(
const buffer_c16_t& channel,
const uint32_t filter_pass_frequency,
const uint32_t filter_stop_frequency
const int32_t filter_low_frequency,
const int32_t filter_high_frequency,
const int32_t filter_transition
);
private:
@@ -53,8 +54,9 @@ private:
bool streaming { false };
std::array<std::complex<float>, 256> channel_spectrum { };
uint32_t channel_spectrum_sampling_rate { 0 };
uint32_t channel_filter_pass_frequency { 0 };
uint32_t channel_filter_stop_frequency { 0 };
int32_t channel_filter_low_frequency { 0 };
int32_t channel_filter_high_frequency { 0 };
int32_t channel_filter_transition { 0 };
void post_message(const buffer_c16_t& data);
+1 -1
View File
@@ -65,7 +65,7 @@ size_t StreamInput::write(const void* const data, const size_t length) {
break;
}
active_buffer = nullptr;
creg::m4txevent::assert();
creg::m4txevent::assert_event();
}
}
+1 -1
View File
@@ -67,7 +67,7 @@ size_t StreamOutput::read(void* const data, const size_t length) {
}
// Tell M0 (IRQ) that a buffer has been consumed.
active_buffer = nullptr;
creg::m4txevent::assert();
creg::m4txevent::assert_event();
}
}
+50 -2
View File
@@ -300,8 +300,8 @@ void encode_frame_velo(ADSBFrame& frame, const uint32_t ICAO_address, const uint
v_rate_coded = (v_rate / 64) + 1;
velo_ew_abs = abs(velo_ew);
velo_ns_abs = abs(velo_ns);
velo_ew_abs = abs(velo_ew) + 1;
velo_ns_abs = abs(velo_ns) + 1;
v_rate_coded_abs = abs(v_rate_coded);
make_frame_adsb(frame, ICAO_address);
@@ -317,4 +317,52 @@ void encode_frame_velo(ADSBFrame& frame, const uint32_t ICAO_address, const uint
frame.make_CRC();
}
// Decoding method from dump1090
adsb_vel decode_frame_velo(ADSBFrame& frame){
adsb_vel velo {false, 0, 0};
uint8_t * frame_data = frame.get_raw_data();
uint8_t velo_type = frame.get_msg_sub();
if(velo_type >= 1 && velo_type <= 4){ //vertical rate is always present
velo.v_rate = (((frame_data[8] & 0x07 ) << 6) | ((frame_data[9]) >> 2) - 1) * 64;
if((frame_data[8] & 0x8) >> 3) velo.v_rate *= -1; //check v_rate sign
}
if(velo_type == 1 || velo_type == 2){ //Ground Speed
int32_t raw_ew = ((frame_data[5] & 0x03) << 8) | frame_data[6];
int32_t velo_ew = raw_ew - 1; //velocities are all offset by one (this is part of the spec)
int32_t raw_ns = ((frame_data[7] & 0x7f) << 3) | (frame_data[8] >> 5);
int32_t velo_ns = raw_ns - 1;
if (velo_type == 2){ // supersonic indicator so multiply by 4
velo_ew = velo_ew << 2;
velo_ns = velo_ns << 2;
}
if(frame_data[5]&0x04) velo_ew *= -1; //check ew direction sign
if(frame_data[7]&0x80) velo_ns *= -1; //check ns direction sign
velo.speed = sqrt(velo_ns*velo_ns + velo_ew*velo_ew);
if(velo.speed){
//calculate heading in degrees from ew/ns velocities
int16_t heading_temp = (int16_t)(atan2(velo_ew,velo_ns) * 180.0 / pi);
// We don't want negative values but a 0-360 scale.
if (heading_temp < 0) heading_temp += 360.0;
velo.heading = (uint16_t)heading_temp;
}
}else if(velo_type == 3 || velo_type == 4){ //Airspeed
velo.valid = frame_data[5] & (1<<2);
velo.heading = ((((frame_data[5] & 0x03)<<8) | frame_data[6]) * 45) << 7;
}
return velo;
}
} /* namespace adsb */
+9
View File
@@ -56,6 +56,13 @@ struct adsb_pos {
int32_t altitude;
};
struct adsb_vel {
bool valid;
int32_t speed; //knot
uint16_t heading; //degree
int32_t v_rate; //ft/min
};
const float CPR_MAX_VALUE = 131072.0;
const float adsb_lat_lut[58] = {
@@ -89,6 +96,8 @@ adsb_pos decode_frame_pos(ADSBFrame& frame_even, ADSBFrame& frame_odd);
void encode_frame_velo(ADSBFrame& frame, const uint32_t ICAO_address, const uint32_t speed,
const float angle, const int32_t v_rate);
adsb_vel decode_frame_velo(ADSBFrame& frame);
//void encode_frame_emergency(ADSBFrame& frame, const uint32_t ICAO_address, const uint8_t code);
void encode_frame_squawk(ADSBFrame& frame, const uint32_t squawk);
+4
View File
@@ -41,6 +41,10 @@ public:
return (raw_data[4] >> 3);
}
uint8_t get_msg_sub() {
return (raw_data[4] & 7);
}
uint32_t get_ICAO_address() {
return (raw_data[1] << 16) + (raw_data[2] << 8) + raw_data[3];
}
+95 -48
View File
@@ -30,15 +30,17 @@
template<size_t N>
struct fir_taps_real {
float pass_frequency_normalized;
float stop_frequency_normalized;
float low_frequency_normalized;
float high_frequency_normalized;
float transition_normalized;
std::array<int16_t, N> taps;
};
template<size_t N>
struct fir_taps_complex {
float pass_frequency_normalized;
float stop_frequency_normalized;
float low_frequency_normalized;
float high_frequency_normalized;
float transition_normalized;
std::array<complex16_t, N> taps;
};
@@ -46,8 +48,9 @@ struct fir_taps_complex {
// IFIR image-reject filter: fs=3072000, pass=8000, stop=344000, decim=8, fout=384000
constexpr fir_taps_real<24> taps_16k0_decim_0 {
.pass_frequency_normalized = 8000.0f / 3072000.0f,
.stop_frequency_normalized = 344000.0f / 3072000.0f,
.low_frequency_normalized = -8000.0f / 3072000.0f,
.high_frequency_normalized = 8000.0f / 3072000.0f,
.transition_normalized = 336000.0f / 3072000.0f,
.taps = { {
1, 67, 165, 340, 599, 944, 1361, 1820,
2278, 2684, 2988, 3152, 3152, 2988, 2684, 2278,
@@ -57,8 +60,9 @@ constexpr fir_taps_real<24> taps_16k0_decim_0 {
// IFIR prototype filter: fs=384000, pass=8000, stop=40000, decim=8, fout=48000
constexpr fir_taps_real<32> taps_16k0_decim_1 {
.pass_frequency_normalized = 8000.0f / 384000.0f,
.stop_frequency_normalized = 40000.0f / 384000.0f,
.low_frequency_normalized = -8000.0f / 384000.0f,
.high_frequency_normalized = 8000.0f / 384000.0f,
.transition_normalized = 32000.0f / 384000.0f,
.taps = { {
-26, -125, -180, -275, -342, -359, -286, -90,
250, 733, 1337, 2011, 2688, 3289, 3740, 3982,
@@ -69,8 +73,9 @@ constexpr fir_taps_real<32> taps_16k0_decim_1 {
// Channel filter: fs=48000, pass=8000, stop=12400, decim=1, fout=48000
constexpr fir_taps_real<32> taps_16k0_channel {
.pass_frequency_normalized = 8000.0f / 48000.0f,
.stop_frequency_normalized = 12400.0f / 48000.0f,
.low_frequency_normalized = -8000.0f / 48000.0f,
.high_frequency_normalized = 8000.0f / 48000.0f,
.transition_normalized = 4400.0f / 48000.0f,
.taps = { {
-73, -285, -376, -8, 609, 538, -584, -1387,
-148, 2173, 1959, -2146, -5267, -297, 12915, 24737,
@@ -83,8 +88,9 @@ constexpr fir_taps_real<32> taps_16k0_channel {
// IFIR image-reject filter: fs=3072000, pass=5500, stop=341500, decim=8, fout=384000
constexpr fir_taps_real<24> taps_11k0_decim_0 {
.pass_frequency_normalized = 5500.0f / 3072000.0f,
.stop_frequency_normalized = 341500.0f / 3072000.0f,
.low_frequency_normalized = -5500.0f / 3072000.0f,
.high_frequency_normalized = 5500.0f / 3072000.0f,
.transition_normalized = 336000.0f / 3072000.0f,
.taps = { {
38, 102, 220, 406, 668, 1004, 1397, 1822,
2238, 2603, 2875, 3020, 3020, 2875, 2603, 2238,
@@ -94,8 +100,9 @@ constexpr fir_taps_real<24> taps_11k0_decim_0 {
// IFIR prototype filter: fs=384000, pass=5500, stop=42500, decim=8, fout=48000
constexpr fir_taps_real<32> taps_11k0_decim_1 {
.pass_frequency_normalized = 5500.0f / 384000.0f,
.stop_frequency_normalized = 42500.0f / 384000.0f,
.low_frequency_normalized = -5500.0f / 384000.0f,
.high_frequency_normalized = 5500.0f / 384000.0f,
.transition_normalized = 37000.0f / 384000.0f,
.taps = { {
-42, -87, -157, -234, -298, -318, -255, -75,
246, 713, 1306, 1976, 2656, 3265, 3724, 3971,
@@ -106,8 +113,9 @@ constexpr fir_taps_real<32> taps_11k0_decim_1 {
// Channel filter: fs=48000, pass=5500, stop=8900, decim=1, fout=48000
constexpr fir_taps_real<32> taps_11k0_channel {
.pass_frequency_normalized = 5500.0f / 48000.0f,
.stop_frequency_normalized = 8900.0f / 48000.0f,
.low_frequency_normalized = -5500.0f / 48000.0f,
.high_frequency_normalized = 5500.0f / 48000.0f,
.transition_normalized = 3400.0f / 48000.0f,
.taps = { {
-68, -345, -675, -867, -582, 247, 1222, 1562,
634, -1379, -3219, -3068, 310, 6510, 13331, 17795,
@@ -120,8 +128,9 @@ constexpr fir_taps_real<32> taps_11k0_channel {
// IFIR image-reject filter: fs=3072000, pass=4250, stop=340250, decim=8, fout=384000
constexpr fir_taps_real<24> taps_4k25_decim_0 {
.pass_frequency_normalized = 4250.0f / 3072000.0f,
.stop_frequency_normalized = 340250.0f / 3072000.0f,
.low_frequency_normalized = -4250.0f / 3072000.0f,
.high_frequency_normalized = 4250.0f / 3072000.0f,
.transition_normalized = 33600.0f / 3072000.0f,
.taps = { {
38, 103, 222, 409, 671, 1006, 1399, 1821,
2236, 2599, 2868, 3012, 3012, 2868, 2599, 2236,
@@ -131,8 +140,9 @@ constexpr fir_taps_real<24> taps_4k25_decim_0 {
// IFIR prototype filter: fs=384000, pass=4250, stop=43750, decim=8, fout=48000
constexpr fir_taps_real<32> taps_4k25_decim_1 {
.pass_frequency_normalized = 4250.0f / 384000.0f,
.stop_frequency_normalized = 43750.0f / 384000.0f,
.low_frequency_normalized = -4250.0f / 384000.0f,
.high_frequency_normalized = 4250.0f / 384000.0f,
.transition_normalized = 39500.0f / 384000.0f,
.taps = { {
-33, -74, -139, -214, -280, -306, -254, -87,
222, 682, 1274, 1951, 2644, 3268, 3741, 3996,
@@ -143,8 +153,9 @@ constexpr fir_taps_real<32> taps_4k25_decim_1 {
// Channel filter: fs=48000, pass=4250, stop=7900, decim=1, fout=48000
constexpr fir_taps_real<32> taps_4k25_channel {
.pass_frequency_normalized = 4250.0f / 48000.0f,
.stop_frequency_normalized = 7900.0f / 48000.0f,
.low_frequency_normalized = -4250.0f / 48000.0f,
.high_frequency_normalized = 4250.0f / 48000.0f,
.transition_normalized = 3650.0f / 48000.0f,
.taps = { {
-58, -14, 153, 484, 871, 1063, 770, -141,
-1440, -2488, -2435, -614, 3035, 7771, 12226, 14927,
@@ -161,8 +172,6 @@ constexpr fir_taps_real<32> taps_4k25_channel {
* sum(abs(taps)): 125270
*/
/*constexpr fir_taps_real<64> taps_64_lp_025_025 {
.pass_frequency_normalized = 0.025f,
.stop_frequency_normalized = 0.025f,
.taps = { {
0, 0, -3, -7, -13, -20, -27, -32,
-34, -33, -25, -10, 13, 47, 94, 152,
@@ -183,8 +192,9 @@ constexpr fir_taps_real<32> taps_4k25_channel {
* sum(abs(taps)): 125270
*/
constexpr fir_taps_real<64> taps_64_lp_025_025 {
.pass_frequency_normalized = 0.0125f,
.stop_frequency_normalized = 0.0125f,
.low_frequency_normalized = 0,
.high_frequency_normalized = 0,
.transition_normalized = 0,
.taps = { {
0, 0, 2, 6, 12, 20, 32, 46,
64, 85, 110, 138, 169, 204, 241, 281,
@@ -201,8 +211,9 @@ constexpr fir_taps_real<64> taps_64_lp_025_025 {
// IFIR image-reject filter: fs=3072000, pass=3000, stop=339000, decim=8, fout=384000
constexpr fir_taps_real<24> taps_6k0_decim_0 {
.pass_frequency_normalized = 3000.0f / 3072000.0f,
.stop_frequency_normalized = 339000.0f / 3072000.0f,
.low_frequency_normalized = -3000.0f / 3072000.0f,
.high_frequency_normalized = 3000.0f / 3072000.0f,
.transition_normalized = 336000.0f / 3072000.0f,
.taps = { {
39, 104, 224, 412, 674, 1008, 1400, 1821,
2234, 2594, 2863, 3006, 3006, 2863, 2594, 2234,
@@ -212,8 +223,9 @@ constexpr fir_taps_real<24> taps_6k0_decim_0 {
// IFIR prototype filter: fs=384000, pass=3000, stop=45000, decim=8, fout=48000
constexpr fir_taps_real<32> taps_6k0_decim_1 {
.pass_frequency_normalized = 3000.0f / 384000.0f,
.stop_frequency_normalized = 45000.0f / 384000.0f,
.low_frequency_normalized = -3000.0f / 384000.0f,
.high_frequency_normalized = 3000.0f / 384000.0f,
.transition_normalized = 43000.0f / 384000.0f,
.taps = { {
-26, -63, -123, -195, -263, -295, -253, -99,
199, 651, 1242, 1927, 2633, 3273, 3760, 4023,
@@ -224,8 +236,9 @@ constexpr fir_taps_real<32> taps_6k0_decim_1 {
// IFIR prototype filter: fs=48000, pass=3000, stop=6700, decim=4, fout=12000
constexpr fir_taps_real<32> taps_6k0_decim_2 {
.pass_frequency_normalized = 3000.0f / 48000.0f,
.stop_frequency_normalized = 6700.0f / 48000.0f,
.low_frequency_normalized = -3000.0f / 48000.0f,
.high_frequency_normalized = 3000.0f / 48000.0f,
.transition_normalized = 3700.0f / 48000.0f,
.taps = { {
95, 178, 247, 208, -21, -474, -1080, -1640,
-1857, -1411, -83, 2134, 4978, 7946, 10413, 11815,
@@ -239,8 +252,9 @@ constexpr fir_taps_real<32> taps_6k0_decim_2 {
* slightly larger than 32767 (33312).
*/
constexpr fir_taps_complex<64> taps_6k0_dsb_channel {
.pass_frequency_normalized = 3000.0f / 12000.0f,
.stop_frequency_normalized = 3300.0f / 12000.0f,
.low_frequency_normalized = -3000.0f / 12000.0f,
.high_frequency_normalized = 3000.0f / 12000.0f,
.transition_normalized = 300.0f / 12000.0f,
.taps = { {
{ -69, 0 }, { -140, 0 }, { 119, 0 }, { 89, 0 },
{ -132, 0 }, { -134, 0 }, { 197, 0 }, { 167, 0 },
@@ -265,8 +279,9 @@ constexpr fir_taps_complex<64> taps_6k0_dsb_channel {
// IFIR prototype filter: fs=12000, pass=3000, stop=3300, decim=1, fout=12000
constexpr fir_taps_complex<64> taps_2k8_usb_channel {
.pass_frequency_normalized = 3000.0f / 12000.0f,
.stop_frequency_normalized = 3300.0f / 12000.0f,
.low_frequency_normalized = 0,
.high_frequency_normalized = 3000.0f / 12000.0f,
.transition_normalized = 300.0f / 12000.0f,
.taps = { {
{ -146, 0 }, { -41, -45 }, { -1, 10 }, { -95, 69 },
{ -194, -41 }, { -91, -158 }, { 14, -43 }, { -150, 67 },
@@ -291,8 +306,9 @@ constexpr fir_taps_complex<64> taps_2k8_usb_channel {
// IFIR prototype filter: fs=12000, pass=3000, stop=3300, decim=1, fout=12000
constexpr fir_taps_complex<64> taps_2k8_lsb_channel {
.pass_frequency_normalized = 3000.0f / 12000.0f,
.stop_frequency_normalized = 3300.0f / 12000.0f,
.low_frequency_normalized = -3000.0f / 12000.0f,
.high_frequency_normalized = 0,
.transition_normalized = 300.0f / 12000.0f,
.taps = { {
{ -146, 0 }, { -41, 45 }, { -1, -10 }, { -95, -69 },
{ -194, 41 }, { -91, 158 }, { 14, 43 }, { -150, -67 },
@@ -313,12 +329,39 @@ constexpr fir_taps_complex<64> taps_2k8_lsb_channel {
} },
};
// USB AM 700Hz filter: fs=12000, start=600, end=800, width=200, stop=40db, decim=1, fout=12000
constexpr fir_taps_complex<64> taps_0k7_usb_channel {
.low_frequency_normalized = 600.0f / 12000.0f,
.high_frequency_normalized = 800.0f / 12000.0f,
.transition_normalized = 200.0f / 12000.0f,
.taps = { {
{ 531, 0 }, { 192, 73 }, { 181, 163 }, { 129, 254 },
{ 34, 328 }, { -97, 364 }, { -251, 345 }, { -403, 261 },
{ -524, 111 }, { -585, -92 }, { -564, -326 }, { -448, -554 },
{ -239, -737 }, { 43, -836 }, { 366, -822 }, { 681, -681 },
{ 936, -417 }, { 1085, -56 }, { 1090, 354 }, { 935, 757 },
{ 629, 1090 }, { 205, 1296 }, { -283, 1331 }, { -766, 1180 },
{ -1172, 851 }, { -1435, 384 }, { -1510, -158 }, { -1377, -702 },
{ -1049, -1165 }, { -568, -1480 }, { 0, -1596 }, { 574, -1496 },
{ 1072, -1191 }, { 1422, -724 }, { 1576, -165 }, { 1515, 406 },
{ 1251, 908 }, { 827, 1273 }, { 309, 1453 }, { -226, 1431 },
{ -703, 1218 }, { -1058, 856 }, { -1248, 405 }, { -1257, -65 },
{ -1100, -489 }, { -810, -810 }, { -441, -992 }, { -53, -1024 },
{ 297, -916 }, { 566, -699 }, { 725, -418 }, { 765, -121 },
{ 697, 148 }, { 546, 355 }, { 348, 479 }, { 138, 517 },
{ -50, 477 }, { -194, 381 }, { -280, 252 }, { -308, 118 },
{ -285, 0 }, { -228, -87 }, { -153, -138 }, { -241, -473 },
} },
};
// WFM 200KF8E emission type //////////////////////////////////////////////
// IFIR image-reject filter: fs=3072000, pass=100000, stop=484000, decim=4, fout=768000
constexpr fir_taps_real<24> taps_200k_wfm_decim_0 = {
.pass_frequency_normalized = 100000.0f / 3072000.0f,
.stop_frequency_normalized = 484000.0f / 3072000.0f,
.low_frequency_normalized = -100000.0f / 3072000.0f,
.high_frequency_normalized = 100000.0f / 3072000.0f,
.transition_normalized = 384000.0f / 3072000.0f,
.taps = { {
48, -18, -151, -364, -557, -548, -139, 789,
2187, 3800, 5230, 6071, 6071, 5230, 3800, 2187,
@@ -328,8 +371,9 @@ constexpr fir_taps_real<24> taps_200k_wfm_decim_0 = {
// IFIR prototype filter: fs=768000, pass=100000, stop=284000, decim=2, fout=384000
constexpr fir_taps_real<16> taps_200k_wfm_decim_1 = {
.pass_frequency_normalized = 100000.0f / 768000.0f,
.stop_frequency_normalized = 284000.0f / 768000.0f,
.low_frequency_normalized = -100000.0f / 768000.0f,
.high_frequency_normalized = 100000.0f / 768000.0f,
.transition_normalized = 184000.0f / 768000.0f,
.taps = { {
-67, -123, 388, 622, -1342, -2185, 4599, 14486,
14486, 4599, -2185, -1342, 622, 388, -123, -67,
@@ -344,8 +388,9 @@ constexpr fir_taps_real<16> taps_200k_wfm_decim_1 = {
* sum(abs(taps)): 125270
*/
constexpr fir_taps_real<64> taps_64_lp_156_198 {
.pass_frequency_normalized = 0.156f,
.stop_frequency_normalized = 0.196f,
.low_frequency_normalized = -0.156f,
.high_frequency_normalized = 0.156f,
.transition_normalized = 0.04f,
.taps = { {
-27, 166, 104, -36, -174, -129, 109, 287,
148, -232, -430, -130, 427, 597, 49, -716,
@@ -362,8 +407,9 @@ constexpr fir_taps_real<64> taps_64_lp_156_198 {
// IFIR image-reject filter: fs=2457600, pass=100000, stop=407200, decim=4, fout=614400
static constexpr fir_taps_real<24> taps_200k_decim_0 = {
.pass_frequency_normalized = 100000.0f / 2457600.0f,
.stop_frequency_normalized = 407200.0f / 2457600.0f,
.low_frequency_normalized = -100000.0f / 2457600.0f,
.high_frequency_normalized = 100000.0f / 2457600.0f,
.transition_normalized = 307200.0f / 2457600.0f,
.taps = { {
90, 94, 4, -240, -570, -776, -563, 309,
1861, 3808, 5618, 6710, 6710, 5618, 3808, 1861,
@@ -373,8 +419,9 @@ static constexpr fir_taps_real<24> taps_200k_decim_0 = {
// IFIR prototype filter: fs=614400, pass=100000, stop=207200, decim=2, fout=307200
static constexpr fir_taps_real<16> taps_200k_decim_1 = {
.pass_frequency_normalized = 100000.0f / 614400.0f,
.stop_frequency_normalized = 207200.0f / 614400.0f,
.low_frequency_normalized = -100000.0f / 614400.0f,
.high_frequency_normalized = 100000.0f / 614400.0f,
.transition_normalized = 107200.0f / 614400.0f,
.taps = { {
-132, -256, 545, 834, -1507, -2401, 4666, 14583,
14583, 4666, -2401, -1507, 834, 545, -256, -132,
+2 -2
View File
@@ -69,7 +69,7 @@ inline void disable() {
#endif
#if defined(LPC43XX_M4)
inline void assert() {
inline void assert_event() {
__SEV();
}
#endif
@@ -93,7 +93,7 @@ inline void disable() {
#endif
#if defined(LPC43XX_M0)
inline void assert() {
inline void assert_event() {
__SEV();
}
#endif
+3 -2
View File
@@ -298,8 +298,9 @@ public:
struct ChannelSpectrum {
std::array<uint8_t, 256> db { { 0 } };
uint32_t sampling_rate { 0 };
uint32_t channel_filter_pass_frequency { 0 };
uint32_t channel_filter_stop_frequency { 0 };
int32_t channel_filter_low_frequency { 0 };
int32_t channel_filter_high_frequency { 0 };
int32_t channel_filter_transition { 0 };
};
using ChannelSpectrumFIFO = FIFO<ChannelSpectrum>;
+2 -2
View File
@@ -26,12 +26,12 @@ using namespace lpc43xx;
#if defined(LPC43XX_M0)
void MessageQueue::signal() {
creg::m0apptxevent::assert();
creg::m0apptxevent::assert_event();
}
#endif
#if defined(LPC43XX_M4)
void MessageQueue::signal() {
creg::m4txevent::assert();
creg::m4txevent::assert_event();
}
#endif
+55 -23
View File
@@ -63,6 +63,10 @@ using modem_repeat_range_t = range_t<int32_t>;
constexpr modem_repeat_range_t modem_repeat_range { 1, 99 };
constexpr int32_t modem_repeat_reset_value { 5 };
using clkout_freq_range_t = range_t<uint32_t>;
constexpr clkout_freq_range_t clkout_freq_range { 10, 60000 };
constexpr uint32_t clkout_freq_reset_value { 10000 };
/* struct must pack the same way on M4 and M0 cores. */
struct data_t {
int64_t tuned_frequency;
@@ -193,7 +197,7 @@ void set_serial_format(const serial_format_t new_value) {
data->serial_format = new_value;
}
static constexpr uint32_t playdead_magic = 0x88d3bb57;
/* static constexpr uint32_t playdead_magic = 0x88d3bb57;
uint32_t playing_dead() {
return data->playing_dead;
@@ -216,47 +220,59 @@ uint32_t playdead_sequence() {
void set_playdead_sequence(const uint32_t new_value) {
data->playdead_sequence = new_value;
data->playdead_magic = playdead_magic;
} */
// ui_config is an uint32_t var storing information bitwise
// bits 0,1,2 store the backlight timer
// bits 31, 30,29,28,27 stores the different single bit configs depicted below
// bits on position 4 to 19 (16 bits) store the clkout frequency
bool clkout_enabled() {
return data->ui_config & (1 << 27);
}
bool config_speaker() {
return (data->ui_config & 0x10000000UL) ? false : true; // Default true
return data->ui_config & (1 << 28);
}
bool stealth_mode() {
return (data->ui_config & 0x20000000UL) ? true : false;
}
void set_config_speaker(bool new_value) {
data->ui_config = (data->ui_config & ~0x10000000UL) | (!new_value << 28);
}
void set_stealth_mode(const bool v) {
data->ui_config = (data->ui_config & ~0x20000000UL) | (v << 29);
}
bool config_splash() {
return (data->ui_config & 0x80000000UL) ? true : false;
return data->ui_config & (1 << 29);
}
bool config_login() {
return (data->ui_config & 0x40000000UL) ? true : false;
return data->ui_config & (1 << 30);
}
bool config_splash() {
return data->ui_config & (1 << 31);
}
uint32_t config_backlight_timer() {
const uint32_t timer_seconds[8] = { 0, 5, 15, 60, 300, 600, 600, 600 };
return timer_seconds[data->ui_config & 0x00000007UL];
const uint32_t timer_seconds[8] = { 0, 5, 15, 30, 60, 180, 300, 600 };
return timer_seconds[data->ui_config & 7]; //first three bits, 8 possible values
}
void set_config_splash(bool v) {
data->ui_config = (data->ui_config & ~0x80000000UL) | (v << 31);
void set_clkout_enabled(bool v) {
data->ui_config = (data->ui_config & ~(1 << 27)) | (v << 27);
}
void set_config_speaker(bool v) {
data->ui_config = (data->ui_config & ~(1 << 28)) | (v << 28);
}
void set_stealth_mode(bool v) {
data->ui_config = (data->ui_config & ~(1 << 29)) | (v << 29);
}
void set_config_login(bool v) {
data->ui_config = (data->ui_config & ~0x40000000UL) | (v << 30);
data->ui_config = (data->ui_config & ~(1 << 30)) | (v << 30);
}
void set_config_splash(bool v) {
data->ui_config = (data->ui_config & ~(1 << 31)) | (v << 31);
}
void set_config_backlight_timer(uint32_t i) {
data->ui_config = (data->ui_config & ~0x00000007UL) | (i & 7);
data->ui_config = (data->ui_config & ~7) | (i & 7);
}
/*void set_config_textentry(uint8_t new_value) {
@@ -287,5 +303,21 @@ void set_pocsag_ignore_address(uint32_t address) {
data->pocsag_ignore_address = address;
}
uint32_t clkout_freq() {
uint16_t freq = (data->ui_config & 0x000FFFF0) >> 4;
if(freq < clkout_freq_range.minimum || freq > clkout_freq_range.maximum) {
data->ui_config = (data->ui_config & ~0x000FFFF0) | clkout_freq_reset_value << 4;
return clkout_freq_reset_value;
}
else {
return freq;
}
}
void set_clkout_freq(uint32_t freq) {
data->ui_config = (data->ui_config & ~0x000FFFF0) | (clkout_freq_range.clip(freq) << 4);
}
} /* namespace persistent_memory */
} /* namespace portapack */
@@ -81,7 +81,7 @@ uint32_t config_backlight_timer();
void set_config_splash(bool v);
void set_config_login(bool v);
void set_config_speaker(bool new_value);
void set_config_speaker(bool v);
void set_config_backlight_timer(uint32_t i);
//uint8_t ui_config_textentry();
@@ -93,6 +93,11 @@ void set_pocsag_last_address(uint32_t address);
uint32_t pocsag_ignore_address();
void set_pocsag_ignore_address(uint32_t address);
bool clkout_enabled();
void set_clkout_enabled(bool v);
uint32_t clkout_freq();
void set_clkout_freq(uint32_t freq);
} /* namespace persistent_memory */
} /* namespace portapack */
+342 -73
View File
@@ -22,9 +22,29 @@
#include "sonde_packet.hpp"
#include "string_format.hpp"
#include <cstring>
//#include <complex>
namespace sonde {
//Defines for Vaisala RS41, from https://github.com/rs1729/RS/blob/master/rs41/rs41sg.c
#define MASK_LEN 64
//Following values include the 4 bytes less shift, consumed in detecting the header on proc_sonde
#define block_status 0x35 //0x039 // 40 bytes
#define block_gpspos 0x10E //0x112 // 21 bytes
#define block_meas 0x61 //0x65 // 42 bytes
#define pos_FrameNb 0x37 //0x03B // 2 byte
#define pos_SondeID 0x39 //0x03D // 8 byte
#define pos_Voltage 0x041 //0x045 // 3 bytes (but first one is the important one) voltage x 10 ie: 26 = 2.6v
#define pos_CalData 0x04E //0x052 // 1 byte, counter 0x00..0x32
#define pos_temp 0x063 //0x067 // 3 bytes (uint24_t)
#define pos_GPSecefX 0x110 //0x114 // 4 byte
#define pos_GPSecefY 0x114 //0x118 // 4 byte (not actually used since Y and Z are following X, and grabbed in that same loop)
#define pos_GPSecefZ 0x118 //0x11C // 4 byte (same as Y)
#define PI 3.1415926535897932384626433832795 //3.1416 //(3.1415926535897932384626433832795)
Packet::Packet(
const baseband::Packet& packet,
const Type type
@@ -48,10 +68,6 @@ size_t Packet::length() const {
return decoder_.symbols_count();
}
bool Packet::is_valid() const {
return true; // TODO
}
Timestamp Packet::received_at() const {
return packet_.timestamp();
}
@@ -60,93 +76,346 @@ Packet::Type Packet::type() const {
return type_;
}
/*uint8_t Packet::vaisala_descramble(const uint32_t pos) {
return reader_raw.read(pos * 8, 8) ^ vaisala_mask[pos & 63];
};*/
//euquiq here:
//RS41SG 320 bits header, 320bytes frame (or more if it is an "extended frame")
//The raw data is xor-scrambled with the values in the 64 bytes vaisala_mask (see.hpp)
//from 0x008 to 0x037 (48 bytes reed-solomon error correction data)
uint32_t Packet::GPS_altitude() const {
uint8_t Packet::vaisala_descramble(const uint32_t pos) const
{ //vaisala_descramble(const uint32_t pos) const {
// packet_[i]; its a bit; packet_.size the total (should be 2560 bits)
uint8_t value = 0;
for (uint8_t i = 0; i < 8; i++)
value = (value << 1) | packet_[(pos * 8) + (7 - i)]; //get the byte from the bits collection
//packetReader reader { packet_ }; //This works just as above.
//value = reader.read(pos * 8,8);
//shift pos because first 4 bytes are consumed by proc_sonde in finding the vaisala signature
uint32_t mask_pos = pos + 4;
value = value ^ vaisala_mask[mask_pos % MASK_LEN]; //descramble with the xor pseudorandom table
return value;
};
GPS_data Packet::get_GPS_data() const
{
GPS_data result;
if ((type_ == Type::Meteomodem_M10) || (type_ == Type::Meteomodem_M2K2))
return (reader_bi_m.read(22 * 8, 32) / 1000) - 48;
else if (type_ == Type::Vaisala_RS41_SG) {
/*uint32_t altitude_ecef = 0;
for (uint32_t i = 0; i < 4; i++)
altitude_ecef = (altitude_ecef << 8) + vaisala_descramble(0x11C + i);*/
// TODO: and a bunch of maths (see ecef2elli() from RS1729)
return 0;
} else
return 0; // Unknown
{
result.alt = (reader_bi_m.read(22 * 8, 32) / 1000) - 48;
result.lat = reader_bi_m.read(14 * 8, 32) / ((1ULL << 32) / 360.0);
result.lon = reader_bi_m.read(18 * 8, 32) / ((1ULL << 32) / 360.0);
}
else if (type_ == Type::Vaisala_RS41_SG)
{
uint8_t XYZ_bytes[4];
int32_t XYZ; // 32bit
double_t X[3];
for (int32_t k = 0; k < 3; k++)
{ //Get X,Y,Z ECEF position from GPS
for (int32_t i = 0; i < 4; i++) //each one is 4 bytes (32 bits)
XYZ_bytes[i] = vaisala_descramble(pos_GPSecefX + (4 * k) + i);
memcpy(&XYZ, XYZ_bytes, 4);
X[k] = XYZ / 100.0;
}
double_t a = 6378137.0;
double_t b = 6356752.31424518;
double_t e = sqrt((a * a - b * b) / (a * a));
double_t ee = sqrt((a * a - b * b) / (b * b));
double_t lam = atan2(X[1], X[0]);
double_t p = sqrt(X[0] * X[0] + X[1] * X[1]);
double_t t = atan2(X[2] * a, p * b);
double_t phi = atan2(X[2] + ee * ee * b * sin(t) * sin(t) * sin(t),
p - e * e * a * cos(t) * cos(t) * cos(t));
double_t R = a / sqrt(1 - e * e * sin(phi) * sin(phi));
result.alt = p / cos(phi) - R;
result.lat = phi * 180 / PI;
result.lon = lam * 180 / PI;
}
return result;
}
float Packet::GPS_latitude() const {
if ((type_ == Type::Meteomodem_M10) || (type_ == Type::Meteomodem_M2K2))
return reader_bi_m.read(14 * 8, 32) / ((1ULL << 32) / 360.0);
//else if (type_ == Type::Vaisala_RS41_SG)
// return vaisala_descramble();
else
return 0; // Unknown
}
float Packet::GPS_longitude() const {
if ((type_ == Type::Meteomodem_M10) || (type_ == Type::Meteomodem_M2K2))
return reader_bi_m.read(18 * 8, 32) / ((1ULL << 32) / 360.0);
else
return 0; // Unknown
}
uint32_t Packet::battery_voltage() const {
uint32_t Packet::battery_voltage() const
{
if (type_ == Type::Meteomodem_M10)
return (reader_bi_m.read(69 * 8, 8) + (reader_bi_m.read(70 * 8, 8) << 8)) * 1000 / 150;
else if (type_ == Type::Meteomodem_M2K2)
return reader_bi_m.read(69 * 8, 8) * 66; // Actually 65.8
return reader_bi_m.read(69 * 8, 8) * 66; // Actually 65.8
else if (type_ == Type::Vaisala_RS41_SG)
{
uint32_t voltage = vaisala_descramble(pos_Voltage) * 100; //byte 69 = voltage * 10 (check if this value needs to be multiplied)
return voltage;
}
else
return 0; // Unknown
}
std::string Packet::type_string() const {
switch (type_) {
case Type::Unknown: return "Unknown";
case Type::Meteomodem_unknown: return "Meteomodem ???";
case Type::Meteomodem_M10: return "Meteomodem M10";
case Type::Meteomodem_M2K2: return "Meteomodem M2K2";
case Type::Vaisala_RS41_SG: return "Vaisala RS41-SG";
default: return "? 0x" + symbols_formatted().data.substr(0, 6);
{
return 0; // Unknown
}
}
std::string Packet::serial_number() const {
if (type() == Type::Meteomodem_M10) {
uint32_t Packet::frame() const
{
if (type_ == Type::Vaisala_RS41_SG)
{
uint32_t frame_number = vaisala_descramble(pos_FrameNb) | (vaisala_descramble(pos_FrameNb + 1) << 8);
return frame_number;
}
else
{
return 0; // Unknown
}
}
temp_humid Packet::get_temp_humid() const
{
temp_humid result;
result.humid = 0;
result.temp = 0;
if ( type_ == Type::Vaisala_RS41_SG && crc_ok_RS41() ) //Only process if packet is healthy
{
//memset(calfrchk, 0, 51); // is this necessary ? only if the sondeID changes (new sonde)
//original code from https://github.com/rs1729/RS/blob/master/rs41/rs41ptu.c
float Rf1, // ref-resistor f1 (750 Ohm)
Rf2, // ref-resistor f2 (1100 Ohm)
co1[3], // { -243.911 , 0.187654 , 8.2e-06 }
calT1[3], // calibration T1
co2[3], // { -243.911 , 0.187654 , 8.2e-06 }
calT2[3], // calibration T2-Hum
calH[2]; // calibration Hum
uint32_t meas[12], i;
//-------------- get_CalData
//-------------- populate calibytes (from getFrameConf)
uint8_t calfr = vaisala_descramble(pos_CalData); //get subframe #slot
for (i = 0; i < 16; i++) //Load subrfame calibration page (16 bytes) into #slot
calibytes[calfr * 16 + i] = vaisala_descramble(pos_CalData + 1 + i); //pos = pos_CalData + 1 + i ; vaisala_descramble(pos)
calfrchk[calfr] = 1; //flag this #slot as populated
memcpy(&Rf1, calibytes + 61, 4); // 0x03*0x10+13
memcpy(&Rf2, calibytes + 65, 4); // 0x04*0x10+ 1
memcpy(co1 + 0, calibytes + 77, 4); // 0x04*0x10+13
memcpy(co1 + 1, calibytes + 81, 4); // 0x05*0x10+ 1
memcpy(co1 + 2, calibytes + 85, 4); // 0x05*0x10+ 5
memcpy(calT1 + 0, calibytes + 89, 4); // 0x05*0x10+ 9
memcpy(calT1 + 1, calibytes + 93, 4); // 0x05*0x10+13
memcpy(calT1 + 2, calibytes + 97, 4); // 0x06*0x10+ 1
memcpy(calH + 0, calibytes + 117, 4); // 0x07*0x10+ 5
memcpy(calH + 1, calibytes + 121, 4); // 0x07*0x10+ 9
memcpy(co2 + 0, calibytes + 293, 4); // 0x12*0x10+ 5
memcpy(co2 + 1, calibytes + 297, 4); // 0x12*0x10+ 9
memcpy(co2 + 2, calibytes + 301, 4); // 0x12*0x10+13
memcpy(calT2 + 0, calibytes + 305, 4); // 0x13*0x10+ 1
memcpy(calT2 + 1, calibytes + 309, 4); // 0x13*0x10+ 5
memcpy(calT2 + 2, calibytes + 313, 4); // 0x13*0x10+ 9
//---------------------------------------
for (i = 0; i < 12; i++)
meas[i] = vaisala_descramble(pos_temp + (3 * i)) |
(vaisala_descramble(pos_temp + (3 * i) + 1) << 8) |
(vaisala_descramble(pos_temp + (3 * i) + 2) << 16);
//----Check if necessary calibytes are already present for calculation
if (calfrchk[0x03] && calfrchk[0x04] && calfrchk[0x04] && calfrchk[0x05] && calfrchk[0x05] && calfrchk[0x06]) //Calibites OK for Temperature
{
//----------get_Tc------------------------
float *p = co1;
float *c = calT1;
float g = (float)(meas[2] - meas[1]) / (Rf2 - Rf1), // gain
Rb = (meas[1] * Rf2 - meas[2] * Rf1) / (float)(meas[2] - meas[1]), // ofs
Rc = meas[0] / g - Rb,
R = Rc * c[0],
T = (p[0] + p[1] * R + p[2] * R * R + c[1]) * (1.0 + c[2]);
result.temp = T;
}
if (calfrchk[0x07])
{
//----------get_RH------------------------
float a0 = 7.5; // empirical
float a1 = 350.0 / calH[0]; // empirical
float fh = (meas[3] - meas[4]) / (float)(meas[5] - meas[4]);
float rh = 100.0 * (a1 * fh - a0);
float T0 = 0.0, T1 = -25.0; // T/C
rh += T0 - result.temp / 5.5; // empir. temperature compensation
if (result.temp < T1)
rh *= 1.0 + (T1 - result.temp) / 90.0; // empir. temperature compensation
if (rh < 0.0)
rh = 0.0;
if (rh > 100.0)
rh = 100.0;
if (result.temp < -273.0)
rh = -1.0;
result.humid = rh;
}
}
return result;
}
std::string Packet::type_string() const
{
switch (type_)
{
case Type::Unknown:
return "Unknown";
case Type::Meteomodem_unknown:
return "Meteomodem ???";
case Type::Meteomodem_M10:
return "Meteomodem M10";
case Type::Meteomodem_M2K2:
return "Meteomodem M2K2";
case Type::Vaisala_RS41_SG:
return "Vaisala RS41-SG";
default:
return "? 0x" + symbols_formatted().data.substr(0, 6);
}
}
std::string Packet::serial_number() const
{
if (type_ == Type::Meteomodem_M10)
{
// See https://github.com/rs1729/RS/blob/master/m10/m10x.c line 606
// Starting at byte #93: 00000000 11111111 22222222 33333333 44444444
// CCCC AAAABBBB
// 44444444 33333333
// DDDEEEEE EEEEEEEE
return to_string_hex(reader_bi_m.read(93 * 8 + 16, 4), 1) +
to_string_dec_uint(reader_bi_m.read(93 * 8 + 20, 4), 2, '0') + " " +
to_string_hex(reader_bi_m.read(93 * 8 + 4, 4), 1) + " " +
to_string_dec_uint(reader_bi_m.read(93 * 8 + 24, 3), 1) +
to_string_dec_uint(reader_bi_m.read(93 * 8 + 27, 13), 4, '0');
} else
to_string_dec_uint(reader_bi_m.read(93 * 8 + 20, 4), 2, '0') + " " +
to_string_hex(reader_bi_m.read(93 * 8 + 4, 4), 1) + " " +
to_string_dec_uint(reader_bi_m.read(93 * 8 + 24, 3), 1) +
to_string_dec_uint(reader_bi_m.read(93 * 8 + 27, 13), 4, '0');
}
else if (type_ == Type::Vaisala_RS41_SG)
{
std::string serial_id = "";
uint8_t achar;
for (uint8_t i = 0; i < 8; i++)
{ //euquiq: Serial ID is 8 bytes long, each byte a char
achar = vaisala_descramble(pos_SondeID + i);
if (achar < 32 || achar > 126)
return "?"; //Maybe there are ids with less than 8 bytes and this is not OK.
serial_id += (char)achar;
}
return serial_id;
}
else
{
return "?";
}
FormattedSymbols Packet::symbols_formatted() const {
return format_symbols(decoder_);
}
bool Packet::crc_ok() const {
switch(type()) {
case Type::Meteomodem_M10: return crc_ok_M10();
default: return false;
}
}
bool Packet::crc_ok_M10() const {
uint16_t cs { 0 };
uint32_t c0, c1, t, t6, t7, s,b ;
for (size_t i = 0; i < packet_.size(); i++) {
FormattedSymbols Packet::symbols_formatted() const
{
if (type_ == Type::Vaisala_RS41_SG)
{ //Euquiq: now we distinguish different types
uint32_t bytes = packet_.size() / 8; //Need the byte amount, which if full, it SHOULD be 320 size() should return 2560
std::string hex_data;
std::string hex_error;
hex_data.reserve(bytes * 2); //2 hexa chars per byte
hex_error.reserve(1);
for (uint32_t i = 0; i < bytes; i++) //log will show the packet starting on the last 4 bytes from signature 93DF1A60
hex_data += to_string_hex(vaisala_descramble(i), 2);
return {hex_data, hex_error};
}
else
{
return format_symbols(decoder_);
}
}
bool Packet::crc_ok() const
{
switch (type_)
{
case Type::Meteomodem_M10:
return crc_ok_M10();
case Type::Vaisala_RS41_SG:
return crc_ok_RS41();
default:
return true; //euquiq: it was false, but if no crc routine, then no way to check
}
}
//each data block has a 2 byte header, data, and 2 byte tail:
// 1st byte: block ID
// 2nd byte: data length (without header or tail)
// <data>
// 2 bytes CRC16 over the data.
bool Packet::crc_ok_RS41() const //check CRC for the data blocks we need
{
if (!crc16rs41(block_status))
return false;
if (!crc16rs41(block_gpspos))
return false;
if (!crc16rs41(block_meas))
return false;
return true;
}
//Checks CRC16 on a RS41 field:
bool Packet::crc16rs41(uint32_t field_start) const
{
int crc16poly = 0x1021;
int rem = 0xFFFF, b, j;
int xbyte;
uint32_t pos = field_start + 1;
uint8_t length = vaisala_descramble(pos);
if (pos + length + 2 > packet_.size() / 8)
return false; //Out of packet!
for (b = 0; b < length; b++)
{
pos++;
xbyte = vaisala_descramble(pos);
rem = rem ^ (xbyte << 8);
for (j = 0; j < 8; j++)
{
if (rem & 0x8000)
{
rem = (rem << 1) ^ crc16poly;
}
else
{
rem = (rem << 1);
}
rem &= 0xFFFF;
}
}
//Check calculated CRC against packet's one
pos++;
int crcok = vaisala_descramble(pos) | (vaisala_descramble(pos + 1) << 8);
if (crcok != rem)
return false;
return true;
}
bool Packet::crc_ok_M10() const
{
uint16_t cs{0};
uint32_t c0, c1, t, t6, t7, s, b;
for (size_t i = 0; i < packet_.size(); i++)
{
b = packet_[i];
c1 = cs & 0xFF;
@@ -165,9 +434,9 @@ bool Packet::crc_ok_M10() const {
c0 = b ^ t ^ s;
cs = ((c1<<8) | c0) & 0xFFFF;
}
cs = ((c1 << 8) | c0) & 0xFFFF;
}
return ((cs & 0xFFFF) == ((packet_[0x63] << 8) | (packet_[0x63 + 1])));
}
+26 -9
View File
@@ -32,6 +32,20 @@
namespace sonde {
static uint8_t calibytes[51*16]; //need these vars to survive
static uint8_t calfrchk[51]; //so subframes are preserved while populated
struct GPS_data {
uint32_t alt { 0 };
float lat { 0 };
float lon { 0 };
};
struct temp_humid {
float temp { 0 };
float humid { 0 };
};
class Packet {
public:
enum class Type : uint32_t {
@@ -41,13 +55,11 @@ public:
Meteomodem_M2K2 = 3,
Vaisala_RS41_SG = 4,
};
Packet(const baseband::Packet& packet, const Type type);
size_t length() const;
bool is_valid() const;
Timestamp received_at() const;
Type type() const;
@@ -55,10 +67,9 @@ public:
std::string serial_number() const;
uint32_t battery_voltage() const;
uint32_t GPS_altitude() const;
float GPS_latitude() const;
float GPS_longitude() const;
GPS_data get_GPS_data() const;
uint32_t frame() const;
temp_humid get_temp_humid() const;
FormattedSymbols symbols_formatted() const;
@@ -75,17 +86,23 @@ private:
0xD0, 0xBC, 0xB4, 0xB6, 0x06, 0xAA, 0xF4, 0x23,
0x78, 0x6E, 0x3B, 0xAE, 0xBF, 0x7B, 0x4C, 0xC1
};
GPS_data ecef_to_gps() const;
//uint8_t vaisala_descramble(const uint32_t pos);
uint8_t vaisala_descramble(uint32_t pos) const;
const baseband::Packet packet_;
const BiphaseMDecoder decoder_;
const FieldReader<BiphaseMDecoder, BitRemapNone> reader_bi_m;
Type type_;
using packetReader = FieldReader<baseband::Packet, BitRemapByteReverse>; //baseband::Packet instead of BiphaseMDecoder
bool crc_ok_M10() const;
bool crc_ok_RS41() const;
bool crc16rs41(uint32_t field_start) const;
};
} /* namespace sonde */
#endif/*__SONDE_PACKET_H__*/
#endif/*__SONDE_PACKET_H__*/
+3 -1
View File
@@ -91,7 +91,9 @@ Rect& Rect::operator-=(const Point& p) {
}
Point polar_to_point(float angle, uint32_t distance) {
return Point(sin_f32(DEG_TO_RAD(angle) + (pi / 2)) * distance, -sin_f32(DEG_TO_RAD(angle)) * distance);
//polar to compass with y negated for screen drawing
return Point(sin_f32(DEG_TO_RAD(-angle) + pi) * distance,
sin_f32(DEG_TO_RAD(-angle) - (pi / 2)) * distance);
}
} /* namespace ui */
+50 -11
View File
@@ -409,10 +409,26 @@ void Labels::paint(Painter& painter) {
void LiveDateTime::on_tick_second() {
rtcGetTime(&RTCD1, &datetime);
text = "";
text = to_string_dec_uint(datetime.month(), 2, '0') + "/" + to_string_dec_uint(datetime.day(), 2, '0') + " " +
to_string_dec_uint(datetime.hour(), 2, '0') + ":" + to_string_dec_uint(datetime.minute(), 2, '0');
if(date_enabled){
text = to_string_dec_uint(datetime.month(), 2, '0') + "/" + to_string_dec_uint(datetime.day(), 2, '0') + " ";
}
text = text + to_string_dec_uint(datetime.hour(), 2, '0') + ":" + to_string_dec_uint(datetime.minute(), 2, '0');
if(seconds_enabled){
text += ":";
if(init_delay==0)
text += to_string_dec_uint(datetime.second(), 2, '0');
else
{
// Placeholder while the seconds are not updated
text += "XX";
init_delay--;
}
}
set_dirty();
}
@@ -444,6 +460,14 @@ void LiveDateTime::paint(Painter& painter) {
);
}
void LiveDateTime::set_date_enabled(bool new_value){
this->date_enabled = new_value;
}
void LiveDateTime::set_seconds_enabled(bool new_value) {
this->seconds_enabled = new_value;
}
/* BigFrequency **********************************************************/
BigFrequency::BigFrequency(
@@ -570,7 +594,10 @@ Console::Console(
{
}
void Console::clear() {
void Console::clear(bool clear_buffer = false) {
if(clear_buffer)
buffer.clear();
display.fill_rectangle(
screen_rect(),
Color::black()
@@ -622,7 +649,8 @@ void Console::write(std::string message) {
void Console::writeln(std::string message) {
write(message);
crlf();
write("\n");
//crlf();
}
void Console::paint(Painter&) {
@@ -799,9 +827,11 @@ bool Checkbox::on_touch(const TouchEvent event) {
Button::Button(
Rect parent_rect,
std::string text
std::string text,
bool instant_exec
) : Widget { parent_rect },
text_ { text }
text_ { text },
instant_exec_ { instant_exec }
{
set_focusable(true);
}
@@ -871,13 +901,22 @@ bool Button::on_touch(const TouchEvent event) {
case TouchEvent::Type::Start:
set_highlighted(true);
set_dirty();
if( on_touch_press) {
on_touch_press(*this);
}
if( on_select && instant_exec_ ) {
on_select(*this);
}
return true;
case TouchEvent::Type::End:
set_highlighted(false);
set_dirty();
if( on_select ) {
if( on_touch_release) {
on_touch_release(*this);
}
if( on_select && !instant_exec_ ) {
on_select(*this);
}
return true;
@@ -1756,13 +1795,13 @@ void VuMeter::paint(Painter& painter) {
lit = true;
if (bar == 0)
color = lit ? Color::red() : Color::dark_red();
color = lit ? Color::red() : Color::dark_grey();
else if (bar == 1)
color = lit ? Color::orange() : Color::dark_orange();
color = lit ? Color::orange() : Color::dark_grey();
else if ((bar == 2) || (bar == 3))
color = lit ? Color::yellow() : Color::dark_yellow();
color = lit ? Color::yellow() : Color::dark_grey();
else
color = lit ? Color::green() : Color::dark_green();
color = lit ? Color::green() : Color::dark_grey();
painter.fill_rectangle({ pos.x(), pos.y() + (Coord)(bar * (LED_height + 1)), width, (Coord)LED_height }, color);
}
+19 -3
View File
@@ -243,7 +243,10 @@ public:
~LiveDateTime();
void paint(Painter& painter) override;
void set_seconds_enabled(bool new_value);
void set_date_enabled(bool new_value);
std::string& string() {
return text;
}
@@ -251,6 +254,10 @@ public:
private:
void on_tick_second();
uint16_t init_delay = 4;
bool date_enabled = true;
bool seconds_enabled = false;
rtc::RTC datetime { };
SignalToken signal_token_tick_second { };
std::string text { };
@@ -312,7 +319,7 @@ class Console : public Widget {
public:
Console(Rect parent_rect);
void clear();
void clear(bool clear_buffer);
void write(std::string message);
void writeln(std::string message);
@@ -371,10 +378,18 @@ private:
class Button : public Widget {
public:
std::function<void(Button&)> on_select { };
std::function<void(Button&)> on_touch_release { }; // Executed when releasing touch, after on_select.
std::function<void(Button&)> on_touch_press { }; // Executed when touching, before on_select.
std::function<bool(Button&, KeyEvent)> on_dir { };
std::function<void(Button&)> on_highlight { };
Button(Rect parent_rect, std::string text);
Button(Rect parent_rect, std::string text, bool instant_exec); // instant_exec: Execute on_select when you touching instead of releasing
Button(
Rect parent_rect,
std::string text
) : Button { parent_rect, text, false }
{
}
Button(
) : Button { { }, { } }
@@ -392,6 +407,7 @@ public:
private:
std::string text_;
bool instant_exec_ { false };
};
class NewButton : public Widget {
Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

+7 -3
View File
@@ -23,7 +23,7 @@ import sys
import struct
from PIL import Image
outfile = open('../../sdcard/world_map.bin', 'wb')
outfile = open('../../sdcard/ADSB/world_map.bin', 'wb')
# Allow for bigger images
Image.MAX_IMAGE_PIXELS = None
@@ -33,6 +33,8 @@ pix = im.load()
outfile.write(struct.pack('H', im.size[0]))
outfile.write(struct.pack('H', im.size[1]))
print("Generating: \t" + outfile.name + "\n from\t\t" + im.filename + "\n please wait...");
for y in range (0, im.size[1]):
line = ''
for x in range (0, im.size[0]):
@@ -45,6 +47,8 @@ for y in range (0, im.size[1]):
# pixel_lcd = (pix[x, y][0] >> 5) << 5
# pixel_lcd |= (pix[x, y][1] >> 5) << 2
# pixel_lcd |= (pix[x, y][2] >> 6)
line += struct.pack('H', pixel_lcd)
outfile.write(line)
line += str(struct.pack('H', pixel_lcd))
outfile.write(line.encode('utf-8'))
print(str(y) + '/' + str(im.size[1]) + '\r', end="")
print("Ready.");

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