Initial import
[SaveMySugar/android-savemysugar.git] / src / main / java / it / ao2 / savemysugar / morse / MorseDistanceModulator.java
1 /*
2  * Represent Morse code symbols using time intervals
3  *
4  * Copyright (C) 2015  Antonio Ospite <ao2@ao2.it>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 package it.ao2.util.morse;
21
22 import java.util.List;
23 import java.util.Vector;
24
25 public class MorseDistanceModulator {
26     public static final double DEFAULT_PERIOD_MIN = 7;
27     public static final double DEFAULT_PERIOD_MAX = 16.5;
28     public static final double DEFAULT_PULSE_MIN = 4.9;
29     public static final double DEFAULT_PULSE_MAX = 5.2;
30     public static final double DEFAULT_INTER_SYMBOL_DISTANCE = DEFAULT_PULSE_MAX;
31
32     private double mPeriodMin;
33     private double mPeriodMax;
34     private double mPulseMin;
35     private double mPulseMax;
36
37     private SymbolTime mDotTime;
38     private SymbolTime mDashTime;
39     private SymbolTime mSignalspaceTime;
40     private SymbolTime mWordspaceTime;
41     private SymbolTime mEOMTime;
42
43     private class SymbolTime {
44         private double dist;
45         private double min;
46         private double max;
47
48         SymbolTime(double periodMin, double periodMax, double multiplier,
49                    double minInterSymbolDistance) {
50             if (multiplier < 0) {
51                 throw new IllegalArgumentException("multiplier must me non-negative");
52             }
53             if (periodMin == periodMax && minInterSymbolDistance == 0) {
54                 throw new IllegalArgumentException("If (periodMin == periodMax) a non zero "
55                                                    + "inter-symbol distance MUST be specified");
56             }
57
58             double symbolDistance = 2 * (periodMax - periodMin);
59             if (symbolDistance == 0) {
60                 symbolDistance = minInterSymbolDistance;
61             }
62
63             dist = minInterSymbolDistance + symbolDistance * multiplier;
64             min = minInterSymbolDistance + periodMin + symbolDistance * multiplier;
65             max = minInterSymbolDistance + periodMin + symbolDistance * (multiplier + 1);
66         }
67
68         SymbolTime(double periodMin, double periodMax, double multiplier) {
69             this(periodMin, periodMax, multiplier, 0.0);
70         }
71
72         public double getDist() {
73             return dist;
74         }
75
76         public double getMin() {
77             return min;
78         }
79
80         public double getMax() {
81             return max;
82         }
83     }
84
85     public MorseDistanceModulator() {
86         setParameters(DEFAULT_PERIOD_MIN,
87                       DEFAULT_PERIOD_MAX,
88                       DEFAULT_PULSE_MIN,
89                       DEFAULT_PULSE_MAX,
90                       DEFAULT_INTER_SYMBOL_DISTANCE);
91     }
92
93     public void setParameters(double newPeriodMin,
94                               double newPeriodMax,
95                               double newPulseMin,
96                               double newPulseMax,
97                               double interSymbolDistance) {
98         mPeriodMin = newPeriodMin;
99         mPeriodMax = newPeriodMax;
100         mPulseMin = newPulseMin;
101         mPulseMax = newPulseMax;
102
103         mDotTime = new SymbolTime(mPeriodMin, mPeriodMax, 0, interSymbolDistance);
104         mDashTime = new SymbolTime(mPeriodMin, mPeriodMax, 1, interSymbolDistance);
105         mSignalspaceTime = new SymbolTime(mPeriodMin, mPeriodMax, 2, interSymbolDistance);
106         mWordspaceTime = new SymbolTime(mPeriodMin, mPeriodMax, 3, interSymbolDistance);
107         mEOMTime = new SymbolTime(mPeriodMin, mPeriodMax, 4, interSymbolDistance);
108     }
109
110     public double getPeriod() {
111         return mPeriodMax;
112     }
113
114     public double symbolToDistance(String symbol) {
115         switch (symbol) {
116         case ".":
117             return mDotTime.getDist();
118         case "-":
119             return mDashTime.getDist();
120         case " ":
121             return mSignalspaceTime.getDist();
122         case "/":
123         case " / ":
124             return mWordspaceTime.getDist();
125         case "EOM":
126             return mEOMTime.getDist();
127         default:
128             throw new IllegalArgumentException("Invalid Morse Symbol: " + symbol);
129         }
130     }
131
132     // Multiple pulses in the same period
133     public boolean isSamePeriod(double pulseDistance) {
134         return (pulseDistance > mPulseMin && pulseDistance <= mPulseMax);
135     }
136
137     public String distanceToSymbol(double distance) {
138         if (distance > mDotTime.getMin() && distance <= mDotTime.getMax()) {
139             return ".";
140         } else if (distance > mDashTime.getMin() && distance <= mDashTime.getMax()) {
141             return "-";
142         } else if (distance > mSignalspaceTime.getMin() && distance <= mSignalspaceTime.getMax()) {
143             return " ";
144         } else if (distance > mWordspaceTime.getMin() && distance <= mWordspaceTime.getMax()) {
145             return "/";
146         } else if (distance > mEOMTime.getMin()) {
147             return "EOM";
148         }
149
150         throw new IllegalArgumentException("Unexpected distance: " + distance);
151     }
152
153     public List<Double> modulate(String morse) {
154         List<Double> distances = new Vector<Double>();
155
156         String[] signals = morse.split(" ");
157         for (int i = 0; i < signals.length; i++) {
158             String signal = signals[i];
159             double distance;
160             for (int j = 0; j < signal.length(); j++) {
161                 String symbol = Character.toString(signal.charAt(j));
162                 distance = symbolToDistance(symbol);
163                 distances.add(distance);
164             }
165             if (i != (signals.length - 1) && !signal.equals("/")  && !signals[i + 1].equals("/")) {
166                 distance = symbolToDistance(" ");
167                 distances.add(distance);
168             }
169         }
170         distances.add(symbolToDistance("EOM"));
171         distances.add(0.0);
172
173         return distances;
174     }
175 }