-
-
Notifications
You must be signed in to change notification settings - Fork 344
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Prioritize module with higher update rates when internal and external module are active #2920
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In preparation of coming changes, I'd strongly prefer these functions to use a module ID (index) rather than explicit names. The reason is that I will start soon to make the module interface as generic as possible, without functions called internal or external, as it basically forces us to double the code or add conditional closes where not necessary.
Talk is cheap, let's show some code :-) This is the kind of API I'm thinking of:
// Fetch the module index of the module responsible for synchro
uint8_t mixerSchedulerGetSyncedModule();
// Fetch the real scheduling period of a given module
uint16_t mixerSchedulerGetPeriod(uint8_t moduleIdx);
Sanity check to see if I understood you right. Changing this
to
The calls will be Same for
to This will return either |
@raphaelcoeffic hope this is what you had in mind |
* Added EL18 specific firmware naming. * fix: Update USB identifier * chore: Add to github builds * fix: Lowercase s in Flysky * chore: Reference EL18 specific firmware file Co-authored-by: Peter Feerick <[email protected]>
the module with the slower update rate follows the faster one (which gets the sync perk). The slower module will be updated at the closed integer dividable rate possible of the faster one without exceeding the max rate of the faster one. 500Hz ext, 143Hz int -> int will get 125Hz (divider 4) 333Hz ext, 143Hz int -> int will get 111Hz (divider 3) 250Hz ext, 143Hz int -> int will get 125Hz (divider 2) 150Hz ext, 143Hz int -> int will get 75Hz (divider 2) 100Hz ext, 143Hz int -> ext will get 71Hz (divider 2)
ebde029
to
93b9e64
Compare
@mha1 I rebased the code on gh pr checkout 2920 --force --recurse-submodules However, I think there are still a couple of issues that need to be solved:
|
|
…d module (failed sync or simu) - UI to getMixerSchedulerRealPeriod() instead of getMixerSchedulerPeriod()
Here's a wild idea to improve 1: Run mixer task at double the masters sync frequency and have the pulse generation run at divider 2 for the master and calculate the slave divider based on the doubled mixer task frequency. Here's a comparison of running the mixer task at the sync frequency to running the mixer task running at double the sync frequency. Check out the slave frequency deviations wrt to their requested frequency. This could be extended to 4 times the sync frequency with even better results, but a less even CPU load and slightly more CPU overhead. |
With ELRS running at 1 kHz this won't work. There is not enough computing time. |
There is no (well a little) additional CPU load as the mixer tasks computation would be distributed over two mixer task cycles. Double the frequency, half the work per cycle. |
I don't think that this will work as intended. What is your idea on how to split the work between the runs? |
roughly: cycle odd: doMixerCalculations() and setupPulses(syncedMaster) The doubling of the frequency merely serves to have a finer resolution for the unsynced slave freq generation, see calculation above. The mixer task running at 2kHz will still result in a 1kHz completion of the mixer computation and pulse generation for the synced module (which asked for 1kHz). |
…e generation run at divider 2 for the master and calculate the slave divider based on the doubled mixer task frequency. doMixerCalculations() and doMixerPeriodicUpdates() run on alternating cycles to distribute CPU load evenly.
tested ok with using ELRS external module/receiver and MPM/Graupner HoTT receiver
I was thinking we could have something like:
As an example:
In that case a multiplier of 4 (instead of 8 for the current implementation) would be chosen. The reason for this is that most modules should be able to tolerate a higher rate than nominal, but might have troubles with a rate that is lower than the nominal rate. For instance, FrSky XJT will start doing strange things when the rate is lower than its nominal rate. Others might fall into failsafe mode, etc.
The heartbeat driver is here: It is activated for instance here: edgetx/radio/src/pulses/pxx1.cpp Lines 236 to 247 in 06c359f
here you can see that when the heartbeat sync is used, the timer is set with an additional 1ms, but we expect the heartbeat to actually trigger earlier: Lines 57 to 62 in 8d5a509
Yes, I believe this it. |
Regarding the heartbeat sync (FrSky modules), I believe we could use it a little differently to better adjust for your proposal. Right now the heartbeat driver directly triggers the mixer, which does not pass the real the rate/frequency to the scheduler. We could however decouple the heartbeat trigger from the mixer schedule, and use it only as a feedback mechanism to compute rate/phase, as provided directly by other modules via telemetry feedback. That would imply computing a corrected rate / delay when the heartbeat ISR is triggered, and feeding this via What do you think? |
Hi Raphael, If you assume there will be problems with updating (some) modules at lower than requested rates your last bullet point shows this is not a solution as there will always be combinations where master freq is higher than requested slave frequencies. If your assumption is correct the concept of following the master freq failed. My assumption was it is ok to serve updates at up to the requested rate, just avoiding to flood the module. If this is correct my thinking was to minimize the deviation real vs requested rate by finding ways to increase the divider resolution. This is the reason why I experimented with doubling the mixer task frequency as this increases the divider resolution, see Excel sheet. It's working. The slave will for most combinations be updated closer to the requested rate but still at lower rates than requested. In general we have to consider three requirements:
The concept of selecting the faster module to drive the mixer task fulfills 1., 2., 3. for the faster module. Checked. Now for the slower module. If your assumption is correct we need to sacrifice at least one of the three requirements as 2. would then be mandatory. We could ensure 2. by making the slower freq generation independent of the master freq, i.e. have a totally asynchronous slave freq generation. This will sacrifice 1. and possibly allow to implement 3. The way to implement this could be a software task running at the requested rate (maybe adjusted with lag data of the slower module). The downside by having to scrap 3 is additional latency jitter for the slower module. What do you think? For FrSky: I think this is the way to go. Sort of unifying the module updating mechanisms. Merry Christmas - Michael |
How about following, have to admit a bit wild, idea: all STM32F2/F4 microcontrollers used in presently supported radios come with 3 internal ADCs. How about dedicating one ADC for the internal module, other for external module and letting them run, incl. the mixer tasks (plural here!) at their native paces? |
Hi Risto, Merry Christmas to you! Thanks for chiming in. No, not that bad of an idea. I actually thought about this too without going into details to deep. My worry was two mixer tasks and pulse generations running could overwhelm the CPU. A quick check with my most complex mixer setup (6 control surface glider with rc-soar like mixer cascading) had a worst case of > 1ms mixer task duration (Tmix max in Statistics/Debug) with rapid movement of the sticks while fast switching between flight modes setup with transition time. I just did it again to take a screenshot and saw 1.36ms Tmin max. The theoretical worst case for the mixer task is ELRS set at 1kHz packet rate (nonsense but no stopping users to do it). 1.36ms is most likely a very rare peak with averages well under 1ms but shows there is not a lot of wiggle room if you want tomaintain a stable 1ms mixer task schedule required for 1kHz packet rate. I decided doubling the computational requirements will not work reliably. Maybe I was to quick but going for the idea will need a close eye on CPU load. Can you verify my observations and conclusion? I think best would be to setup timing measurements for the mixer tasks and lower periodic task and see if they meet their deadlines under stress conditions. Michael PS: I measured the menus task (50ms period) some time ago with 2.7 and a non-trivial model setup idling but with active logging. Measurement showed menus task completion times varying from 15-35ms meaning roughly 25%-30% reserve. Not an awful lot. |
…k scheduling by timer interrupt scheme
Here's some timing measurements of the mixer task with ELRS running at the worst case scenario 1000Hz ELRS and MPM requesting 143Hz and a model with my most complex rc-soar like 6 control surfaces glider setup. This setup was tested successfully with BetaFPV 2.4 (and 868) module/receiver and MPM running Graupner HoTT with Grupner HoTT receiver. The 1000Hz/143Hz combination will result in the mixer task running at 2kHz with a synced ELRS pulse generation at 1000Hz (divider 2) and the MPM running at 142 Hz (divider 14). The total mixer task processing is spread over two cycles, one even, one odd cycle. At every even cycle the mixer calculations and synchronous pulse generation are performed. This takes about 455us with the system idling, i.e. no flight mode transitions, no stick movement, etc. It is worth to note here that this duration is independent of the mixer frequency. So running a 1kHz update rate in combination with a complex mixer setup even without a second module active will at least sporadically overwhelm the CPU and will lead to other tasks suffering with 2.8 release firmware. 1kHz needs to be considered as a "you know what you are doing" mode and will only provide reliable and constant latency results in combination with low complexity mixer setups. Tmin Max in Statistics is a good indicator. It should stay well under 0.6ms to have a reasonable CPU margin left for other tasks. Back to this PR. Here's a screenshot showing the two cycle strategy and the mixer calculations and synchronous pulse generation using above described setup: A closer look at the even cycle: This shows the frequency of the asynchronous pulse generation. With the above mentioned setup (divider 14) 142Hz (7000us period) are expected. Looking closer at the penalty this PR adds. It's about 700ns in every even and odd cycle to decide if it's time to generate asynchronous pulses. Here's an example where asynchronous pulses need to be generated in an odd cycle. In total having a second module active adds about 20us max penalty. Takeaways:
Open points:
If you are interested in taking a closer look at the measurements:
|
nuked by the serial/module API renovation |
# Conflicts: # radio/src/gui/colorlcd/crossfire_settings.cpp # radio/src/gui/colorlcd/radio_version.cpp # radio/src/gui/common/stdlcd/radio_version.cpp # radio/src/mixer_scheduler.cpp # radio/src/mixer_scheduler.h # radio/src/tasks/mixer_task.cpp # radio/src/tests/mixer_scheduler.cpp
closed in favor of #3642 |
This implements enhancement proposal #2919
Summary of changes:
If both modules are active the one with the higher update rate drives the mixer task and hence also receives the perk of being synced. The slower module will be updated at an integer fraction (divider) of the higher rate module. The divider is calculated such that the resulting update rate for the slower module will be as close as possible to its requested rate but doesn’t exceed it. It doesn’t matter if the higher update rate module is internal or external. The higher update rate module will always drive the mixer task, the slower one will follow.
Example: TX16s external ELSR and internal MPM (requests 7000us update period):
500Hz ELRS /143Hz MPM-> 500Hz sync/125Hz no sync (divider 4)
333Hz ELRS/143Hz MPM -> 333Hz sync/111Hz no sync (divider 3)
100Hz ELRS/143Hz MPM -> 71Hz no sync (divider 2)/143Hz sync
Telemetry data of both modules will be received and is discoverable.
This PR also contains a change to the Radio Version Modules/RX version pop-up to show the real update rates for the internal and external module.
Current implementation showing internal MPM driving the mixer schedule period and forcing the external module to the same update rate while the external module is set to 500Hz. Internal module is master:
New implementation with ELRS set at 500Hz and MPM active. ELRS is master:
New implementation with ELRS set at 333Hz and MPM active. ELRS is master:
New implementation with ELRS set at 250Hz and MPM active. ELRS is master:
New implementation with ELRS set at 150Hz and MPM active. ELRS is master:
New implementation with ELRS set at 100Hz and MPM active. MPM is master:
New implementation with ELRS set at 50Hz and MPM active. MPM is master: