1141cc406Sopenharmony_ci/* sane - Scanner Access Now Easy.
2141cc406Sopenharmony_ci
3141cc406Sopenharmony_ci   Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
4141cc406Sopenharmony_ci
5141cc406Sopenharmony_ci   This file is part of the SANE package.
6141cc406Sopenharmony_ci
7141cc406Sopenharmony_ci   This program is free software; you can redistribute it and/or
8141cc406Sopenharmony_ci   modify it under the terms of the GNU General Public License as
9141cc406Sopenharmony_ci   published by the Free Software Foundation; either version 2 of the
10141cc406Sopenharmony_ci   License, or (at your option) any later version.
11141cc406Sopenharmony_ci
12141cc406Sopenharmony_ci   This program is distributed in the hope that it will be useful, but
13141cc406Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
14141cc406Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15141cc406Sopenharmony_ci   General Public License for more details.
16141cc406Sopenharmony_ci
17141cc406Sopenharmony_ci   You should have received a copy of the GNU General Public License
18141cc406Sopenharmony_ci   along with this program.  If not, see <https://www.gnu.org/licenses/>.
19141cc406Sopenharmony_ci*/
20141cc406Sopenharmony_ci
21141cc406Sopenharmony_ci#ifndef BACKEND_GENESYS_MOTOR_H
22141cc406Sopenharmony_ci#define BACKEND_GENESYS_MOTOR_H
23141cc406Sopenharmony_ci
24141cc406Sopenharmony_ci#include <algorithm>
25141cc406Sopenharmony_ci#include <cstdint>
26141cc406Sopenharmony_ci#include <vector>
27141cc406Sopenharmony_ci#include "enums.h"
28141cc406Sopenharmony_ci#include "sensor.h"
29141cc406Sopenharmony_ci#include "value_filter.h"
30141cc406Sopenharmony_ci
31141cc406Sopenharmony_cinamespace genesys {
32141cc406Sopenharmony_ci
33141cc406Sopenharmony_ci/*  Describes a motor acceleration curve.
34141cc406Sopenharmony_ci
35141cc406Sopenharmony_ci    Definitions:
36141cc406Sopenharmony_ci        v - speed in steps per pixeltime
37141cc406Sopenharmony_ci        w - speed in pixel times per step. w = 1 / v
38141cc406Sopenharmony_ci        a - acceleration in steps per pixeltime squared
39141cc406Sopenharmony_ci        s - distance travelled in steps
40141cc406Sopenharmony_ci        t - time in pixeltime
41141cc406Sopenharmony_ci
42141cc406Sopenharmony_ci    The physical mode defines the curve in physical quantities. We assume that the scanner head
43141cc406Sopenharmony_ci    accelerates from standstill to the target speed uniformly. Then:
44141cc406Sopenharmony_ci
45141cc406Sopenharmony_ci    v(t) = v(0) + a * t                                                                         (2)
46141cc406Sopenharmony_ci
47141cc406Sopenharmony_ci    Where `a` is acceleration, `t` is time. Also we can calculate the travelled distance `s`:
48141cc406Sopenharmony_ci
49141cc406Sopenharmony_ci    s(t) = v(0) * t + a * t^2 / 2                                                               (3)
50141cc406Sopenharmony_ci
51141cc406Sopenharmony_ci    The actual motor slope is defined as the duration of each motor step. That means we need to
52141cc406Sopenharmony_ci    define speed in terms of travelled distance.
53141cc406Sopenharmony_ci
54141cc406Sopenharmony_ci    Solving (3) for `t` gives:
55141cc406Sopenharmony_ci
56141cc406Sopenharmony_ci           sqrt( v(0)^2 + 2 * a * s ) - v(0)
57141cc406Sopenharmony_ci    t(s) = ---------------------------------                                                    (4)
58141cc406Sopenharmony_ci                          a
59141cc406Sopenharmony_ci
60141cc406Sopenharmony_ci    Combining (4) and (2) will yield:
61141cc406Sopenharmony_ci
62141cc406Sopenharmony_ci    v(s) = sqrt( v(0)^2 + 2 * a * s )                                                           (5)
63141cc406Sopenharmony_ci
64141cc406Sopenharmony_ci    The data in the slope struct MotorSlope corresponds to the above in the following way:
65141cc406Sopenharmony_ci
66141cc406Sopenharmony_ci    maximum_start_speed is `w(0) = 1/v(0)`
67141cc406Sopenharmony_ci
68141cc406Sopenharmony_ci    maximum_speed is defines maximum speed which should not be exceeded
69141cc406Sopenharmony_ci
70141cc406Sopenharmony_ci    minimum_steps is not used
71141cc406Sopenharmony_ci
72141cc406Sopenharmony_ci    g is `a`
73141cc406Sopenharmony_ci
74141cc406Sopenharmony_ci    Given the start and target speeds on a known motor curve, `a` can be computed as follows:
75141cc406Sopenharmony_ci
76141cc406Sopenharmony_ci        v(t1)^2 - v(t0)^2
77141cc406Sopenharmony_ci    a = -----------------                                                                       (6)
78141cc406Sopenharmony_ci               2 * s
79141cc406Sopenharmony_ci
80141cc406Sopenharmony_ci    Here `v(t0)` and `v(t1)` are the start and target speeds and `s` is the number of step required
81141cc406Sopenharmony_ci    to reach the target speeds.
82141cc406Sopenharmony_ci*/
83141cc406Sopenharmony_cistruct MotorSlope
84141cc406Sopenharmony_ci{
85141cc406Sopenharmony_ci    // initial speed in pixeltime per step
86141cc406Sopenharmony_ci    unsigned initial_speed_w = 0;
87141cc406Sopenharmony_ci
88141cc406Sopenharmony_ci    // max speed in pixeltime per step
89141cc406Sopenharmony_ci    unsigned max_speed_w = 0;
90141cc406Sopenharmony_ci
91141cc406Sopenharmony_ci    // maximum number of steps in the table
92141cc406Sopenharmony_ci    unsigned max_step_count;
93141cc406Sopenharmony_ci
94141cc406Sopenharmony_ci    // acceleration in steps per pixeltime squared.
95141cc406Sopenharmony_ci    float acceleration = 0;
96141cc406Sopenharmony_ci
97141cc406Sopenharmony_ci    unsigned get_table_step_shifted(unsigned step, StepType step_type) const;
98141cc406Sopenharmony_ci
99141cc406Sopenharmony_ci    static MotorSlope create_from_steps(unsigned initial_w, unsigned max_w,
100141cc406Sopenharmony_ci                                        unsigned steps);
101141cc406Sopenharmony_ci};
102141cc406Sopenharmony_ci
103141cc406Sopenharmony_cistruct MotorSlopeTable
104141cc406Sopenharmony_ci{
105141cc406Sopenharmony_ci    std::vector<std::uint16_t> table;
106141cc406Sopenharmony_ci
107141cc406Sopenharmony_ci    void slice_steps(unsigned count, unsigned step_multiplier);
108141cc406Sopenharmony_ci
109141cc406Sopenharmony_ci    // expands the table by the given number of steps
110141cc406Sopenharmony_ci    void expand_table(unsigned count, unsigned step_multiplier);
111141cc406Sopenharmony_ci
112141cc406Sopenharmony_ci    std::uint64_t pixeltime_sum() const { return pixeltime_sum_; }
113141cc406Sopenharmony_ci
114141cc406Sopenharmony_ci    void generate_pixeltime_sum();
115141cc406Sopenharmony_ciprivate:
116141cc406Sopenharmony_ci    std::uint64_t pixeltime_sum_ = 0;
117141cc406Sopenharmony_ci};
118141cc406Sopenharmony_ci
119141cc406Sopenharmony_ciunsigned get_slope_table_max_size(AsicType asic_type);
120141cc406Sopenharmony_ci
121141cc406Sopenharmony_ciMotorSlopeTable create_slope_table_for_speed(const MotorSlope& slope, unsigned target_speed_w,
122141cc406Sopenharmony_ci                                             StepType step_type, unsigned steps_alignment,
123141cc406Sopenharmony_ci                                             unsigned min_size, unsigned max_size);
124141cc406Sopenharmony_ci
125141cc406Sopenharmony_cistd::ostream& operator<<(std::ostream& out, const MotorSlope& slope);
126141cc406Sopenharmony_ci
127141cc406Sopenharmony_cistruct MotorProfile
128141cc406Sopenharmony_ci{
129141cc406Sopenharmony_ci    MotorProfile() = default;
130141cc406Sopenharmony_ci    MotorProfile(const MotorSlope& a_slope, StepType a_step_type, unsigned a_max_exposure) :
131141cc406Sopenharmony_ci        slope{a_slope}, step_type{a_step_type}, max_exposure{a_max_exposure}
132141cc406Sopenharmony_ci    {}
133141cc406Sopenharmony_ci
134141cc406Sopenharmony_ci    MotorSlope slope;
135141cc406Sopenharmony_ci    StepType step_type = StepType::FULL;
136141cc406Sopenharmony_ci    int motor_vref = -1;
137141cc406Sopenharmony_ci
138141cc406Sopenharmony_ci    // the resolutions this profile is good for
139141cc406Sopenharmony_ci    ValueFilterAny<unsigned> resolutions = VALUE_FILTER_ANY;
140141cc406Sopenharmony_ci    // the scan method this profile is good for. If the list is empty, good for any method.
141141cc406Sopenharmony_ci    ValueFilterAny<ScanMethod> scan_methods = VALUE_FILTER_ANY;
142141cc406Sopenharmony_ci
143141cc406Sopenharmony_ci    unsigned max_exposure = 0; // 0 - any exposure
144141cc406Sopenharmony_ci};
145141cc406Sopenharmony_ci
146141cc406Sopenharmony_cistd::ostream& operator<<(std::ostream& out, const MotorProfile& profile);
147141cc406Sopenharmony_ci
148141cc406Sopenharmony_cistruct Genesys_Motor
149141cc406Sopenharmony_ci{
150141cc406Sopenharmony_ci    Genesys_Motor() = default;
151141cc406Sopenharmony_ci
152141cc406Sopenharmony_ci    // id of the motor description
153141cc406Sopenharmony_ci    MotorId id = MotorId::UNKNOWN;
154141cc406Sopenharmony_ci    // motor base steps. Unit: 1/inch
155141cc406Sopenharmony_ci    int base_ydpi = 0;
156141cc406Sopenharmony_ci    // slopes to derive individual slopes from
157141cc406Sopenharmony_ci    std::vector<MotorProfile> profiles;
158141cc406Sopenharmony_ci    // slopes to derive individual slopes from for fast moving
159141cc406Sopenharmony_ci    std::vector<MotorProfile> fast_profiles;
160141cc406Sopenharmony_ci
161141cc406Sopenharmony_ci    MotorSlope& get_slope_with_step_type(StepType step_type)
162141cc406Sopenharmony_ci    {
163141cc406Sopenharmony_ci        for (auto& p : profiles) {
164141cc406Sopenharmony_ci            if (p.step_type == step_type)
165141cc406Sopenharmony_ci                return p.slope;
166141cc406Sopenharmony_ci        }
167141cc406Sopenharmony_ci        throw SaneException("No motor profile with step type");
168141cc406Sopenharmony_ci    }
169141cc406Sopenharmony_ci
170141cc406Sopenharmony_ci    const MotorSlope& get_slope_with_step_type(StepType step_type) const
171141cc406Sopenharmony_ci    {
172141cc406Sopenharmony_ci        for (const auto& p : profiles) {
173141cc406Sopenharmony_ci            if (p.step_type == step_type)
174141cc406Sopenharmony_ci                return p.slope;
175141cc406Sopenharmony_ci        }
176141cc406Sopenharmony_ci        throw SaneException("No motor profile with step type");
177141cc406Sopenharmony_ci    }
178141cc406Sopenharmony_ci
179141cc406Sopenharmony_ci    StepType max_step_type() const
180141cc406Sopenharmony_ci    {
181141cc406Sopenharmony_ci        if (profiles.empty()) {
182141cc406Sopenharmony_ci            throw std::runtime_error("Profiles table is empty");
183141cc406Sopenharmony_ci        }
184141cc406Sopenharmony_ci        StepType step_type = StepType::FULL;
185141cc406Sopenharmony_ci        for (const auto& p : profiles) {
186141cc406Sopenharmony_ci            step_type = static_cast<StepType>(
187141cc406Sopenharmony_ci                    std::max(static_cast<unsigned>(step_type),
188141cc406Sopenharmony_ci                             static_cast<unsigned>(p.step_type)));
189141cc406Sopenharmony_ci        }
190141cc406Sopenharmony_ci        return step_type;
191141cc406Sopenharmony_ci    }
192141cc406Sopenharmony_ci};
193141cc406Sopenharmony_ci
194141cc406Sopenharmony_cistd::ostream& operator<<(std::ostream& out, const Genesys_Motor& motor);
195141cc406Sopenharmony_ci
196141cc406Sopenharmony_ci} // namespace genesys
197141cc406Sopenharmony_ci
198141cc406Sopenharmony_ci#endif // BACKEND_GENESYS_MOTOR_H
199