Change to PlatformIO

This commit is contained in:
jupfi 2022-01-06 16:33:37 +01:00
commit b3ffde53d1
33 changed files with 4067 additions and 0 deletions

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

7
.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,7 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
]
}

5
include/Debug.h Normal file
View file

@ -0,0 +1,5 @@
#ifdef DEBUG
#define DEBUG_PRINT(x) Serial.println (x)
#else
#define DEBUG_PRINT(x)
#endif

27
include/Pins.h Normal file
View file

@ -0,0 +1,27 @@
//SPI Pins
#define SCLK_PIN 14
#define MOSI_PIN 13
#define MISO_PIN 12
//ADF Pins
#define LE_PIN 27
#define CE_PIN 25
// Pins M1
#define EN_PIN_M1 25
#define DIR_PIN_M1 33
#define STEP_PIN_M1 32
#define CS_PIN_M1 26
#define DIAG1_PIN_M1 2 // used for homing
// Pins M2
#define EN_PIN_M2 17
#define DIR_PIN_M2 19
#define STEP_PIN_M2 5
#define CS_PIN_M2 18
#define DIAG1_PIN_M2 4 // used for homing
//ADC Pin
#define REFLECTION_PIN 15

14
include/Positions.h Normal file
View file

@ -0,0 +1,14 @@
// Position @ 50, 75, 100, 125, 150, 175, 200 MHz
struct FREQUENCY_POSITION {
uint32_t FREQUENCY;
uint32_t TUNING_POSITION;
uint32_t MATCHING_POSITION;
};
// Settings for 100MHz -18dB
#define TUNING_STEPPER_HOME 34100U
#define MATCHING_STEPPER_HOME 32000U
// Settings for 125MHz -30dB
//#define TUNING_STEPPER_HOME 37550U
//#define MATCHING_STEPPER_HOME 29500U

39
include/README Normal file
View file

@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

12
include/Stepper.h Normal file
View file

@ -0,0 +1,12 @@
// Stepper Settings
#define STEPS_PER_ROTATION 3200U // 200 * 16 -> Microstepping
// Stall Detection sensitivity
#define STALL_VALUE 16 // [-64..63]
struct Stepper{
AccelStepper STEPPER;
TMC2130Stepper DRIVER;
int STALL_PIN;
int HOME_POSITION;
};

336
lib/ADF4351/ADF4351.cpp Normal file
View file

@ -0,0 +1,336 @@
#include "ADF4351.h"
#define ADF_FREQ_MAX 4294967295UL ///< Maximum Generated Frequency = value of MAX Unsigned Long
#define ADF_FREQ_MIN 34385000UL ///< Minimum Generated Frequency
#define ADF_PFD_MAX 32000000.0 ///< Maximum Frequency for Phase Detector
#define ADF_PFD_MIN 125000.0 ///< Minimum Frequency for Phase Detector
#define ADF_REFIN_MAX 250000000UL ///< Maximum Reference Frequency
#define REF_FREQ_DEFAULT 250000000UL ///< Default Reference Frequency
uint32_t steps[] = { 1000, 5000, 10000, 50000, 100000 , 500000, 1000000 }; ///< Array of Allowed Step Values (Hz)
/*!
single register constructor
*/
Reg::Reg()
{
whole = 0 ;
}
uint32_t Reg::get()
{
return whole ;
}
void Reg::set(uint32_t value)
{
whole = value ;
}
void Reg::setbf(uint8_t start, uint8_t len, uint32_t value)
{
uint32_t bitmask = ((1UL << len) - 1UL) ;
value &= bitmask ;
bitmask <<= start ;
whole = ( whole & ( ~bitmask)) | ( value << start ) ;
}
uint32_t Reg::getbf(uint8_t start, uint8_t len)
{
uint32_t bitmask = ((1UL << len) - 1UL) << start ;
uint32_t result = ( whole & bitmask) >> start ;
return ( result ) ;
}
// Constructor function; initializes communication pinouts
ADF4351::ADF4351(int SCLK, int DATA, int LE, int CE){
_sclk = SCLK;
_data=DATA;
_le=LE;
_ce=CE;
SPIClass SPI1(HSPI);
spi_settings = SPISettings(1000000UL, MSBFIRST, SPI_MODE0) ;
reffreq = REF_FREQ_DEFAULT ;
cfreq = 0 ;
ChanStep = steps[0] ;
RD2refdouble = 0 ;
RCounter = 25 ;
RD1Rdiv2 = 0 ;
BandSelClock = 80 ;
ClkDiv = 150 ;
Prescaler = 0 ;
pwrlevel = 0 ;
}
void ADF4351::begin(void){
pinMode(_sclk, OUTPUT);
pinMode(_data, OUTPUT);
pinMode(_le, OUTPUT);
pinMode(_ce, OUTPUT);
SPI1.begin();
}
int ADF4351::setf(uint32_t freq)
{
// calculate settings from freq
if ( freq > ADF_FREQ_MAX ) return 1 ;
if ( freq < ADF_FREQ_MIN ) return 1 ;
int localosc_ratio = 2200000000UL / freq ;
outdiv = 1 ;
int RfDivSel = 0 ;
// select the output divider
while ( outdiv <= localosc_ratio && outdiv <= 64 ) {
outdiv *= 2 ;
RfDivSel++ ;
}
if ( freq > 3600000000UL/outdiv )
Prescaler = 1 ;
else
Prescaler = 0 ;
PFDFreq = (float) reffreq * ( (float) ( 1.0 + RD2refdouble) / (float) (RCounter * (1.0 + RD1Rdiv2))); // find the loop freq
BigNumber::begin(10) ;
char tmpstr[20] ;
// kludge - BigNumber doesn't like leading spaces
// so you need to make sure the string passed doesnt
// have leading spaces.
int cntdigits = 0 ;
uint32_t num = (uint32_t) ( PFDFreq / 10000 ) ;
while ( num != 0 ) {
cntdigits++ ;
num /= 10 ;
}
dtostrf(PFDFreq, cntdigits + 8 , 3, tmpstr) ;
// end of kludge
BigNumber BN_PFDFreq = BigNumber(tmpstr) ;
BigNumber BN_N = ( BigNumber(freq) * BigNumber(outdiv) ) / BN_PFDFreq ;
N_Int = (uint16_t) ( (uint32_t) BN_N ) ;
BigNumber BN_Mod = BN_PFDFreq / BigNumber(ChanStep) ;
Mod = BN_Mod ;
BN_Mod = BigNumber(Mod) ;
BigNumber BN_Frac = ((BN_N - BigNumber(N_Int)) * BN_Mod) + BigNumber("0.5") ;
Frac = (int) ( (uint32_t) BN_Frac);
BN_N = BigNumber(N_Int) ;
if ( Frac != 0 ) {
uint32_t gcd = gcd_iter(Frac, Mod) ;
if ( gcd > 1 ) {
Frac /= gcd ;
BN_Frac = BigNumber(Frac) ;
Mod /= gcd ;
BN_Mod = BigNumber(Mod) ;
}
}
BigNumber BN_cfreq ;
if ( Frac == 0 ) {
BN_cfreq = ( BN_PFDFreq * BN_N) / BigNumber(outdiv) ;
} else {
BN_cfreq = ( BN_PFDFreq * ( BN_N + ( BN_Frac / BN_Mod) ) ) / BigNumber(outdiv) ;
}
cfreq = BN_cfreq ;
if ( cfreq != freq ) Serial.println(F("output freq diff than requested")) ;
BigNumber::finish() ;
if ( Mod < 2 || Mod > 4095) {
Serial.println(F("Mod out of range")) ;
return 1 ;
}
if ( (uint32_t) Frac > (Mod - 1) ) {
Serial.println(F("Frac out of range")) ;
return 1 ;
}
if ( Prescaler == 0 && ( N_Int < 23 || N_Int > 65535)) {
Serial.println(F("N_Int out of range")) ;
return 1;
} else if ( Prescaler == 1 && ( N_Int < 75 || N_Int > 65535 )) {
Serial.println(F("N_Int out of range")) ;
return 1;
}
// setting the registers to default values
// R0
R[0].set(0UL) ;
// (0,3,0) control bits
R[0].setbf(3, 12, Frac) ; // fractonal
R[0].setbf(15, 16, N_Int) ; // N integer
// R1
R[1].set(0UL) ;
R[1].setbf(0, 3, 1) ; // control bits
R[1].setbf(3, 12, Mod) ; // Mod
R[1].setbf(15, 12, 1); // phase
R[1].setbf(27, 1, Prescaler); // prescaler
// (28,1,0) phase adjust
// R2
R[2].set(0UL) ;
R[2].setbf(0, 3, 2) ; // control bits
// (3,1,0) counter reset
// (4,1,0) cp3 state
// (5,1,0) power down
R[2].setbf(6, 1, 1) ; // pd polarity
if ( Frac == 0 ) {
R[2].setbf(7, 1, 1) ; // LDP, int-n mode
R[2].setbf(8, 1, 1) ; // ldf, int-n mode
} else {
R[2].setbf(7, 1, 0) ; // LDP, frac-n mode
R[2].setbf(8, 1, 0) ; // ldf ,frac-n mode
}
R[2].setbf(9, 4, 7) ; // charge pump
// (13,1,0) dbl buf
R[2].setbf(14, 10, RCounter) ; // r counter
R[2].setbf(24, 1, RD1Rdiv2) ; // RD1_RDiv2
R[2].setbf(25, 1, RD2refdouble) ; // RD2refdouble
// R[2].setbf(26,3,0) ; // muxout, not used
// (29,2,0) low noise and spurs mode
// R3
R[3].set(0UL) ;
R[3].setbf(0, 3, 3) ; // control bits
R[3].setbf(3, 12, ClkDiv) ; // clock divider
// (15,2,0) clk div mode
// (17,1,0) reserved
// (18,1,0) CSR
// (19,2,0) reserved
if ( Frac == 0 ) {
R[3].setbf(21, 1, 1); // charge cancel, reduces pfd spurs
R[3].setbf(22, 1, 1); // ABP, int-n
} else {
R[3].setbf(21, 1, 0) ; // charge cancel
R[3].setbf(22, 1, 0); // ABP, frac-n
}
R[3].setbf(23, 1, 1) ; // Band Select Clock Mode
// (24,8,0) reserved
// R4
R[4].set(0UL) ;
R[4].setbf(0, 3, 4) ; // control bits
R[4].setbf(3, 2, pwrlevel) ; // output power 0-3 (-4dbM to 5dbM, 3db steps)
R[4].setbf(5, 1, 1) ; // rf output enable
// (6,2,0) aux output power
// (8,1,0) aux output enable
// (9,1,0) aux output select
// (10,1,0) mtld
// (11,1,0) vco power down
R[4].setbf(12, 8, BandSelClock) ; // band select clock divider
R[4].setbf(20, 3, RfDivSel) ; // rf divider select
R[4].setbf(23, 1, 1) ; // feedback select
// (24,8,0) reserved
// R5
R[5].set(0UL) ;
R[5].setbf(0, 3, 5) ; // control bits
// (3,16,0) reserved
R[5].setbf(19, 2, 3) ; // Reserved field,set to 11
// (21,1,0) reserved
R[5].setbf(22, 2, 1) ; // LD Pin Mode
// (24,8,0) reserved
int i ;
SPI1.beginTransaction(spi_settings);
for (i = 5 ; i > -1 ; i--) {
WriteRegister(getReg(i)) ;
//delayMicroseconds(2500) ;
}
SPI1.endTransaction();
return 0 ; // ok
}
int ADF4351::setrf(uint32_t f)
{
if ( f > ADF_REFIN_MAX ) return 1 ;
if ( f < 100000UL ) return 1 ;
float newfreq = (float) f * ( (float) ( 1.0 + RD2refdouble) / (float) (RCounter * (1.0 + RD1Rdiv2))); // check the loop freq
if ( newfreq > ADF_PFD_MAX ) return 1 ;
if ( newfreq < ADF_PFD_MIN ) return 1 ;
reffreq = f ;
return 0 ;
}
// write data into register
void ADF4351::WriteRegister(uint32_t regData){
/*digitalWrite(_le, LOW);
_regData = regData;
for(int i=0; i<32; i++)
{
if(((_regData<<i)&0x80000000)==0x80000000)
{
digitalWrite(_data,1);
}
else
{
digitalWrite(_data,0) ;
}
digitalWrite(_sclk, HIGH);
delay(1);
digitalWrite(_sclk, LOW);
}
// load data into register
digitalWrite(_le, HIGH);
delay(1);
digitalWrite(_le, LOW);*/
byte txbyte ;
digitalWrite(_le, LOW) ;
for (int i = 3 ; i > -1 ; i--) {
txbyte = (byte) (regData >> (i * 8)) ;
SPI1.transfer(txbyte) ;
}
digitalWrite(_le, HIGH) ;
delayMicroseconds(5) ;
digitalWrite(_le, LOW) ;
}
uint32_t ADF4351::getReg(int n)
{
return R[n].whole ;
}
uint32_t ADF4351::gcd_iter(uint32_t u, uint32_t v)
{
uint32_t t;
while (v) {
t = u ;
u = v ;
v = t % v ;
}
return u ;
}

114
lib/ADF4351/ADF4351.h Normal file
View file

@ -0,0 +1,114 @@
#ifndef __ADF4351_H__
#define __ADF4351_H__
#include <Arduino.h>
#include <stdint.h>
#include <Wire.h>
#include <SPI.h>
#include <BigNumber.h>
/*!
@brief Stores a device register value
This class is used to store and manage a single ADF4351 register value
and provide bit field manipulations.
*/
class Reg {
public:
/*!
Constructor
*/
Reg();
/*!
get the current register value
returns same value as getbf(0,32)
@return unsigned long value of the register
*/
uint32_t get();
/*!
sets the register value
@param value unsigned long
*/
void set(uint32_t value);
/*!
current register value
returns same value as getbf(0,32)
*/
uint32_t whole;
/*!
modifies the register value based on a value and bitfield (mask)
@param start index of the bit to start the modification
@param len length number of bits to modify
@param value value to modify (value is truncated if larger than len bits)
*/
void setbf(uint8_t start, uint8_t len , uint32_t value);
/*!
gets the current register bitfield value, based on the start and length mask
@param start index of the bit of where to start
@param len length number of bits to get
@return bitfield value
*/
uint32_t getbf(uint8_t start, uint8_t len);
};
class ADF4351{
public:
ADF4351(int SCLK, int DATA, int LE, int CE);
void begin(void);
void setReferenceFrequency(uint32_t reffreq);
void WriteRegister(uint32_t regData);
int setf(uint32_t freq) ; // set freq
int setrf(uint32_t f) ; // set reference freq
uint32_t getReg(int n) ;
uint32_t gcd_iter(uint32_t u, uint32_t v) ;
Reg R[6] ;
SPISettings spi_settings;
SPIClass SPI1;
uint32_t reffreq;
uint32_t cfreq ;
uint16_t N_Int ;
int Frac ;
uint32_t Mod ;
float PFDFreq ;
uint32_t ChanStep;
int outdiv ;
uint8_t RD2refdouble ;
int RCounter ;
uint8_t RD1Rdiv2 ;
uint8_t BandSelClock ;
int ClkDiv ;
uint8_t Prescaler ;
byte pwrlevel ;
private:
int _data, _sclk, _le, _ce;
long _regData;
};
#endif

21
lib/BigNumber/LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017-2018 S. Downey
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

58
lib/BigNumber/README.md Normal file
View file

@ -0,0 +1,58 @@
BigNumber
=========
BigNumber library for Arduino devices.
Use 256-bit numbers on tiny platforms.
ported from https://github.com/nickgammon/BigNumber
## Requirements
- Any Arduino Device: (recommended model) [Adafruit Feather HUZZAH with ESP8266 WiFi](https://www.adafruit.com/product/2821)
- Arduino IDE: https://www.arduino.cc/en/Main/Software
## Usage
### Initialize the BigNumber library
```cpp
BigNumber.begin();
```
### create your BigNumber
```cpp
BigNumber bigNum;
bigNum = "67411411944031530524562029520200239450929984281572279077458355238873731477537";
```
```cpp
BigNumber bigNum = "67411411944031530524562029520200239450929984281572279077458355238873731477537";
```
### modify your BigNumber
```cpp
BigNumber bigNum = "67411411944031530524562029520200239450929984281572279077458355238873731477537";
BigNumber base = 16;
BigNumber multiplier = base.pow(8);
bigNum *= multiplier;
```
### Serial.print your BigNumber
```cpp
BigNumber bigNum = "67411411944031530524562029520200239450929984281572279077458355238873731477537";
Serial.println(bigNum);
```
> 67411411944031530524562029520200239450929984281572279077458355238873731477537
#
# More documentation and examples:
http://www.gammon.com.au/forum/?id=11519

View file

@ -0,0 +1,32 @@
// BigNumber test: calculate e
#include "BigNumber.h"
void setup ()
{
Serial.begin (115200);
Serial.println ();
BigNumber::begin (50); // max around 160 on the Uno
// some big numbers
BigNumber n = 1, e = 1, one = 1;
int i = 1;
BigNumber E; // previous result
unsigned long start = millis ();
do
{
E = e;
n *= i++; // n is i factorial
e += one / n;
} while (e != E);
unsigned long time = millis () - start;
Serial.println (e);
Serial.print (time);
Serial.println (" mS");
} // end of setup
void loop () { }

View file

@ -0,0 +1,25 @@
// BigNumber test: factorials
#include "BigNumber.h"
void setup ()
{
Serial.begin (115200);
while (!Serial) ;
delay(500);
Serial.println ();
BigNumber::begin (); // initialize library
//factorials
BigNumber fact = 1;
for (int i = 2; i <= 200; i++)
{
Serial.print(i);
Serial.print("! = ");
fact *= i;
Serial.println(fact);
}
} // end of setup
void loop () { }

View file

@ -0,0 +1,20 @@
// BigNumber test: multiplication
#include "BigNumber.h"
void setup ()
{
Serial.begin (115200);
Serial.println ();
Serial.println ();
BigNumber::begin (); // initialize library
// test multiplication
BigNumber a = "564328376";
BigNumber b = "18254546";
BigNumber c = a * b;
Serial.println (c);
} // end of setup
void loop () { }

View file

@ -0,0 +1,49 @@
// BigNumber test: calculate natural logarithm
#include "BigNumber.h"
BigNumber one (1);
BigNumber two (2);
BigNumber half ("0.5");
BigNumber ln (BigNumber x)
{
// if x <= 0.5 or >= 2 take the square root and double the result
if (x <= half || x >= two)
{
BigNumber e = ln (x.sqrt ());
return e * two;
} // end of x <= 0.5 or >= 2
// some big numbers
BigNumber a = (x - one) / (x + one);
BigNumber e;
BigNumber t = a;
BigNumber i = one;
BigNumber E;
do
{
E = e;
e += t / i;
t *= a * a;
i += two;
} while (e != E);
return e * two;
} // end of function ln
void setup ()
{
Serial.begin (115200);
Serial.println ();
Serial.println ();
BigNumber::begin (45); // max around 45 on the Uno for ln (100)
Serial.println (ln (100));
} // end of setup
void loop () { }

View file

@ -0,0 +1,60 @@
// BigNumber example: Calculate Pi using the Francois Viete formula
#include <BigNumber.h>
BigNumber PiViete ()
{
// Francois Viete formula
BigNumber two (2); // used as a constant value of 2
BigNumber s (0); // used in iteration
BigNumber t (1); // used in iteration
for (int i = 0; i < 115; i++) // number of iterations: 10 to 115
{
BigNumber r = s + two; // temporary value
s = r.sqrt ();
t *= s / two;
}
return two / t; // calculate pi
} // end of function pi
void setup()
{
Serial.begin (115200);
Serial.println ();
// Arduino Uno
// -----------
// The Arduino Uno has only 2k bytes of SRAM.
// The maximum length of BigNumber is 74 for an Arduino Uno.
// About 115 iterations in the Francois Viete formula is enough for an Arduino Uno.
// That will take 34 seconds.
//
// Arduino Zero or Arduino MKR
// ---------------------------
// For an Arduino Zero, it is possible to set the length of BigNumber to a higher value.
// For example, the length of BigNumber to 1000, and 1500 iterations.
// That will take 8 hours.
//
BigNumber::begin (74); // Length of the BigNumber. 50 is fast, 74 is slow.
Serial.println (F("Please wait patiently (34 seconds with Arduino Uno)"));
Serial.println (F("pi (as text) = 3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706"));
unsigned long start = millis();
BigNumber pi = PiViete ();
unsigned long finish = millis();
Serial.print (F("pi (Viete formula) = "));
Serial.println (pi);
Serial.print (F("It took "));
Serial.print ((finish - start) / 1000UL);
Serial.print (F(" seconds."));
} // end of setup
void loop () { }

View file

@ -0,0 +1,43 @@
// BigNumber test: calculate power of E
#include "BigNumber.h"
BigNumber ePower (BigNumber x)
{
// if x > 1 halve it and square the result
if (x > 1)
{
BigNumber e = ePower (x / BigNumber (2));
return e * e;
} // end of x > 1
// some big numbers
BigNumber n = 1, e = 1, t = 1;
int i = 1;
BigNumber E;
do
{
E = e;
n *= i++; // n is i factorial
t *= x; // t is x^i
e += t / n;
} while (e != E);
return e;
} // end of function ePower
void setup ()
{
Serial.begin (115200);
Serial.println ();
Serial.println ();
BigNumber::begin (65); // max around 65 on the Uno for a power of 5
Serial.println (ePower (5)); // e ^ 5
} // end of setup
void loop () { }

View file

@ -0,0 +1,38 @@
// BigNumber test: powers
#include "BigNumber.h"
void setup ()
{
Serial.begin (115200);
Serial.println ();
BigNumber::begin (); // initialize library
Serial.println ("--- powers of 2 ---");
BigNumber a = 2;
for (int i = 1; i <= 300; i++)
{
Serial.print ("2^");
Serial.print (i);
Serial.print (" = ");
BigNumber p = a.pow (i);
Serial.println (p);
} // end of for loop
Serial.println ("--- powers of 3 ---");
a = 3;
for (int i = 1; i <= 300; i++)
{
Serial.print ("3^");
Serial.print (i);
Serial.print (" = ");
BigNumber p = a.pow (i);
Serial.println (p);
} // end of for loop
} // end of setup
void loop () { }

View file

@ -0,0 +1,43 @@
// BigNumber test: calculate sines
#include "BigNumber.h"
// calculate sine of x with 'precision' iterations
BigNumber sine (const BigNumber x, BigNumber precision)
{
BigNumber val = 1;
const BigNumber one = 1, two = 2;
while (precision > 0)
{
val = one - val * x * x / (two * precision) / (two * precision + one);
precision--;
}
val = x * val;
return val;
} // end of function sine
void setup ()
{
Serial.begin (115200);
Serial.println ();
Serial.println ();
BigNumber::begin (50);
BigNumber E ("2.7182818284590452353602874713526624977572470936999595749669676277240766303535");
BigNumber pi ("3.1415926535897932384626433832795028841971693993751058209749445923078164062862");
for (int deg = 0; deg <= 90; deg++)
{
Serial.print ("sin(");
Serial.print (deg);
Serial.print (") = ");
BigNumber rad (deg);
rad *= pi / BigNumber (180);
Serial.println (sine (rad, 30));
} // end of for loop
} // end of setup
void loop () { }

339
lib/BigNumber/gpl.txt Normal file
View file

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View file

@ -0,0 +1,11 @@
BigNumber KEYWORD1
begin KEYWORD2
finish KEYWORD2
setScale KEYWORD2
toString KEYWORD2
isNegative KEYWORD2
isZero KEYWORD2
isNearZero KEYWORD2
sqrt KEYWORD2
pow KEYWORD2
divMod KEYWORD2

View file

@ -0,0 +1,10 @@
name=BigNumber
version=3.5
author=NickGammon
maintainer=NickGammon
sentence=BigNumber library for the Arduino
category=Other
url=https://github.com/nickgammon/BigNumber
architectures=*
paragraph=A library for handling "big" numbers, similar to the "bc" program under Unix. Numbers consisting of large numbers of digits, and large numbers of decimal places can be manipulated, subject to memory limitations.
include=BigNumber.h

View file

@ -0,0 +1,12 @@
//
// BigNumber.h
//
// Author: Nick Gammon
// Date: 7th January 2018.
// Contributors: Paul Stoffregen, S. Downey
// Version: 3.5
// Released into the public domain.
#pragma once
#include "BigNumber/BigNumber.h"

View file

@ -0,0 +1,245 @@
//
// BigNumber.cpp
//
// Author: Nick Gammon
// Date: 22nd January 2013.
// Contributors: Paul Stoffregen, S. Downey
// Version: 3.5
// Released into the public domain.
// Added print function as suggested by Paul Stoffregen.
// Changed printTo to return the length, as required.
#include "BigNumber.h"
int BigNumber::scale_ = 0;
// constructor
BigNumber::BigNumber () : num_ (NULL)
{
bc_init_num (&num_); // default to zero
} // end of constructor from string
// constructor
BigNumber::BigNumber (const char * s) : num_ (NULL)
{
bc_str2num(&num_, s, scale_);
} // end of constructor from string
BigNumber::BigNumber (const int n) : num_ (NULL) // constructor from int
{
bc_int2num (&num_, n);
} // end of constructor from int
// copy constructor
BigNumber::BigNumber (const BigNumber & rhs)
{
if (this != &rhs)
num_ = bc_copy_num (rhs.num_);
} // end of BigNumber::BigNumber
//operator=
BigNumber & BigNumber::operator= (const BigNumber & rhs)
{
// gracefully handle self-assignment (eg. a = a;)
if (this == &rhs )
return *this;
bc_free_num (&num_); // get rid of old one
num_ = bc_copy_num (rhs.num_);
return *this;
} // end of BigNumber::BigNumber & operator=
// destructor - free memory used, if any
BigNumber::~BigNumber ()
{
bc_free_num (&num_);
} // end of destructor
// set scale factor (number of places after the decimal point)
int BigNumber::setScale (const int scale)
{
int old_scale = scale_;
if (scale >= 0)
scale_ = scale;
else
scale_ = 0;
return old_scale;
} // end of BigNumber::setScale
// initialize package
// supply scale (number of decimal places): default zero
void BigNumber::begin (const int scale)
{
bc_init_numbers ();
scale_ = scale;
} // end of BigNumber::begin
// finished with package
// free special numbers: zero, one, two
void BigNumber::finish ()
{
bc_free_numbers ();
} // end of BigNumber::finish
// return a pointer to a string containing the number
// MUST FREE THIS after use!
// eg: char * s = mynumber.toString ();
// Serial.println (s);
// free (s);
char * BigNumber::toString () const
{
return bc_num2str(num_);
} // end of BigNumber::toString
BigNumber::operator long () const
{
return bc_num2long (num_);
} // end of BigNumber::operator long
// Allow Arduino's Serial.print() to print BigNumber objects!
size_t BigNumber::printTo(Print& p) const
{
char *buf = bc_num2str(num_);
size_t len = p.write(buf);
free(buf);
return len;
}
// add
BigNumber & BigNumber::operator+= (const BigNumber & n)
{
bc_num result = NULL;
bc_add (num_, n.num_, &result, scale_);
bc_free_num (&num_);
num_ = result;
return *this;
} // end of BigNumber::operator+=
// subtract
BigNumber & BigNumber::operator-= (const BigNumber & n)
{
bc_num result = NULL;
bc_sub (num_, n.num_, &result, scale_);
bc_free_num (&num_);
num_ = result;
return *this;
} // end of BigNumber::operator-=
// divide
BigNumber & BigNumber::operator/= (const BigNumber & n)
{
bc_num result = NULL;
bc_init_num (&result); // in case zero
bc_divide (num_, n.num_, &result, scale_);
bc_free_num (&num_);
num_ = result;
return *this;
} // end of BigNumber::operator/=
// multiply
BigNumber & BigNumber::operator*= (const BigNumber & n)
{
bc_num result = NULL;
bc_multiply (num_, n.num_, &result, scale_);
bc_free_num (&num_);
num_ = result;
return *this;
} // end of BigNumber::operator*=
// modulo
BigNumber & BigNumber::operator%= (const BigNumber & n)
{
bc_num result = NULL;
bc_init_num (&result); // in case zero
bc_modulo (num_, n.num_, &result, scale_);
bc_free_num (&num_);
num_ = result;
return *this;
} // end of BigNumber::operator%=
// ----------------------------- COMPARISONS ------------------------------
// compare less with another BigNumber
bool BigNumber::operator< (const BigNumber & rhs) const
{
return bc_compare (num_, rhs.num_) < 0;
} // end of BigNumber::operator<
// compare greater with another BigNumber
bool BigNumber::operator> (const BigNumber & rhs) const
{
return bc_compare (num_, rhs.num_) > 0;
} // end of BigNumber::operator>
// compare less-or-equal with another BigNumber
bool BigNumber::operator<= (const BigNumber & rhs) const
{
return bc_compare (num_, rhs.num_) <= 0;
} // end of BigNumber::operator<=
// compare greater-or-equal with another BigNumber
bool BigNumber::operator>= (const BigNumber & rhs) const
{
return bc_compare (num_, rhs.num_) >= 0;
} // end of BigNumber::operator>=
// compare not equal with another BigNumber
bool BigNumber::operator!= (const BigNumber & rhs) const
{
return bc_compare (num_, rhs.num_) != 0;
} // end of BigNumber::operator!=
// compare equal with another BigNumber
bool BigNumber::operator== (const BigNumber & rhs) const
{
return bc_compare (num_, rhs.num_) == 0;
} // end of BigNumber::operator==
// special comparisons
bool BigNumber::isNegative () const
{
return bc_is_neg (num_) == true;
} // end of BigNumber::isNegative
bool BigNumber::isZero () const
{
return bc_is_zero (num_) == true;
} // end of BigNumber::isZero
bool BigNumber::isNearZero () const
{
return bc_is_near_zero (num_, scale_) == true;
} // end of BigNumber::isNearZero
// ----------------------------- OTHER OPERATIONS ------------------------------
// square root
BigNumber BigNumber::sqrt () const
{
BigNumber result (*this);
bc_sqrt (&result.num_, scale_);
return result;
} // end of BigNumber::sqrt
// raise to power
BigNumber BigNumber::pow (const BigNumber power) const
{
BigNumber result;
bc_raise (num_, power.num_, &result.num_, scale_);
return result;
} // end of BigNumber::pow
void BigNumber::divMod (const BigNumber divisor, BigNumber & quotient, BigNumber & remainder) const
{
bc_divmod (num_, divisor.num_, &quotient.num_, &remainder.num_, scale_);
}
// raise number by power, modulus modulus
BigNumber BigNumber::powMod (const BigNumber power, const BigNumber & modulus) const
{
BigNumber result;
bc_raisemod (num_, power.num_, modulus.num_, &result.num_, scale_);
return result;
}

View file

@ -0,0 +1,115 @@
//
// BigNumber.h
//
// Author: Nick Gammon
// Date: 22nd January 2013.
// Contributors: Paul Stoffregen, S. Downey
// Version: 3.5
// Released into the public domain.
// Added print function as suggested by Paul Stoffregen.
#ifndef _BigNumber_h
#define _BigNumber_h
#include <stddef.h>
#include <Arduino.h>
extern "C"
{
#include "number.h"
}
class BigNumber : public Printable
{
// the current scaling amount - shared amongst all BigNumbers
static int scale_;
// member variable (the big number)
bc_num num_;
public:
// constructors
BigNumber (); // default constructor
BigNumber (const char * s); // constructor from string
BigNumber (const int n); // constructor from int
// copy constructor
BigNumber (const BigNumber & rhs);
// destructor
~BigNumber ();
// static methods: initialize package, and set global scaling factor
static void begin (const int scale = 0);
static void finish (); // free memory used by 'begin' method
static int setScale (const int scale = 0);
// for outputting purposes ...
char * toString () const; // returns number as string, MUST FREE IT after use!
operator long () const;
virtual size_t printTo(Print& p) const; // for Arduino Serial.print()
// operators ... assignment
BigNumber & operator= (const BigNumber & rhs);
// operations on the number which change it (eg. a += 5; )
BigNumber & operator+= (const BigNumber & n);
BigNumber & operator-= (const BigNumber & n);
BigNumber & operator/= (const BigNumber & n);
BigNumber & operator*= (const BigNumber & n);
BigNumber & operator%= (const BigNumber & n); // modulo
// operations on the number which do not change it (eg. a = b + 5; )
BigNumber operator+ (const BigNumber & n) const { BigNumber temp = *this; temp += n; return temp; };
BigNumber operator- (const BigNumber & n) const { BigNumber temp = *this; temp -= n; return temp; };
BigNumber operator/ (const BigNumber & n) const { BigNumber temp = *this; temp /= n; return temp; };
BigNumber operator* (const BigNumber & n) const { BigNumber temp = *this; temp *= n; return temp; };
BigNumber operator% (const BigNumber & n) const { BigNumber temp = *this; temp %= n; return temp; };
// prefix operations
BigNumber & operator++ () { *this += 1; return *this; }
BigNumber & operator-- () { *this -= 1; return *this; }
// postfix operations (cannot return by reference)
// we make a temporary object, change our current object, return the temporary one
// if we returned a reference it would cease to exist, so we have to return a copy
BigNumber operator++ (int) { BigNumber temp = *this; *this += 1; return temp; }
BigNumber operator-- (int) { BigNumber temp = *this; *this -= 1; return temp; }
// comparisons
bool operator< (const BigNumber & rhs) const;
bool operator< (const int rhs) const { return *this < BigNumber (rhs); }
bool operator> (const BigNumber & rhs) const;
bool operator> (const int rhs) const { return *this > BigNumber (rhs); }
bool operator<= (const BigNumber & rhs) const;
bool operator<= (const int rhs) const { return *this <= BigNumber (rhs); }
bool operator>= (const BigNumber & rhs) const;
bool operator>= (const int rhs) const { return *this >= BigNumber (rhs); }
bool operator!= (const BigNumber & rhs) const;
bool operator!= (const int rhs) const { return *this != BigNumber (rhs); }
bool operator== (const BigNumber & rhs) const;
bool operator== (const int rhs) const { return *this == BigNumber (rhs); }
// quick sign test
bool isNegative () const;
// quick zero test
bool isZero () const;
// In some places we need to check if the number is almost zero.
// Specifically, all but the last digit is 0 and the last digit is 1.
// Last digit is defined by scale.
bool isNearZero () const;
// other mathematical operations
BigNumber sqrt () const;
BigNumber pow (const BigNumber power) const;
// divide number by divisor, give quotient and remainder
void divMod (const BigNumber divisor, BigNumber & quotient, BigNumber & remainder) const;
// raise number by power, modulus modulus
BigNumber powMod (const BigNumber power, const BigNumber & modulus) const;
}; // end class declaration
#endif

View file

@ -0,0 +1,24 @@
/*
* config.h
* number.c from GNU bc-1.06 exports some symbols without the bc_ prefix.
* This header file fixes this without touching either number.c or number.h
* (luckily, number.c already wants to include a config.h).
* Clients of number.c should include config.h before number.h.
*/
#include <string.h>
#include <limits.h>
#define NDEBUG 1
#define _zero_ bc_zero
#define _one_ bc_one
#define _two_ bc_two
#define num2str bc_num2str
#define mul_base_digits bc_mul_base_digits
#define bc_rt_warn bc_error
#define bc_rt_error bc_error
#define bc_out_of_memory() bc_error(0)
void bc_error(const int mesg);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,161 @@
/* number.h: Arbitrary precision numbers header file. */
/*
Copyright (C) 1991, 1992, 1993, 1994, 1997, 2000 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License , or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to:
The Free Software Foundation, Inc.
59 Temple Place, Suite 330
Boston, MA 02111-1307 USA.
You may contact the author by:
e-mail: philnelson@acm.org
us-mail: Philip A. Nelson
Computer Science Department, 9062
Western Washington University
Bellingham, WA 98226-9062
*************************************************************************/
#ifndef _NUMBER_H_
#define _NUMBER_H_
// errors, warnings
#define BC_ERROR_OUT_OF_MEMORY 0
#define BC_ERROR_EXPONENT_TOO_LARGE_IN_RAISE 1
#define BC_WARNING_NON_ZERO_SCALE_IN_EXPONENT -1
#define BC_WARNING_NON_ZERO_SCALE_IN_BASE -2
#define BC_WARNING_NON_ZERO_SCALE_IN_MODULUS -3
typedef enum {PLUS, MINUS} sign;
typedef struct bc_struct *bc_num;
typedef struct bc_struct
{
sign n_sign;
int n_len; /* The number of digits before the decimal point. */
int n_scale; /* The number of digits after the decimal point. */
int n_refs; /* The number of pointers to this number. */
bc_num n_next; /* Linked list for available list. */
char *n_ptr; /* The pointer to the actual storage.
If NULL, n_value points to the inside of
another number (bc_multiply...) and should
not be "freed." */
char *n_value; /* The number. Not zero char terminated.
May not point to the same place as n_ptr as
in the case of leading zeros generated. */
} bc_struct;
/* The base used in storing the numbers in n_value above.
Currently this MUST be 10. */
#define BASE 10
/* Some useful macros and constants. */
#define CH_VAL(c) (c - '0')
#define BCD_CHAR(d) (d + '0')
#ifdef MIN
#undef MIN
#undef MAX
#endif
#define MAX(a,b) ((a)>(b)?(a):(b))
#define MIN(a,b) ((a)>(b)?(b):(a))
#define ODD(a) ((a)&1)
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
#ifndef LONG_MAX
#define LONG_MAX 0x7ffffff
#endif
/* Global numbers. */
extern bc_num _zero_;
extern bc_num _one_;
extern bc_num _two_;
/* Function Prototypes */
/* Define the _PROTOTYPE macro if it is needed. */
#ifndef _PROTOTYPE
#define _PROTOTYPE(func, args) func args
#endif
_PROTOTYPE(void bc_init_numbers, (void));
_PROTOTYPE(void bc_free_numbers, (void));
_PROTOTYPE(bc_num bc_new_num, (int length, int scale));
_PROTOTYPE(void bc_free_num, (bc_num *num));
_PROTOTYPE(bc_num bc_copy_num, (bc_num num));
_PROTOTYPE(void bc_init_num, (bc_num *num));
_PROTOTYPE(void bc_str2num, (bc_num *num, const char *str, int scale));
_PROTOTYPE(char *bc_num2str, (bc_num num));
_PROTOTYPE(void bc_int2num, (bc_num *num, int val));
_PROTOTYPE(long bc_num2long, (bc_num num));
_PROTOTYPE(int bc_compare, (bc_num n1, bc_num n2));
_PROTOTYPE(char bc_is_zero, (bc_num num));
_PROTOTYPE(char bc_is_near_zero, (bc_num num, int scale));
_PROTOTYPE(char bc_is_neg, (bc_num num));
_PROTOTYPE(void bc_add, (bc_num n1, bc_num n2, bc_num *result, int scale_min));
_PROTOTYPE(void bc_sub, (bc_num n1, bc_num n2, bc_num *result, int scale_min));
_PROTOTYPE(void bc_multiply, (bc_num n1, bc_num n2, bc_num *prod, int scale));
_PROTOTYPE(int bc_divide, (bc_num n1, bc_num n2, bc_num *quot, int scale));
_PROTOTYPE(int bc_modulo, (bc_num num1, bc_num num2, bc_num *result,
int scale));
_PROTOTYPE(int bc_divmod, (bc_num num1, bc_num num2, bc_num *quot,
bc_num *rem, int scale));
_PROTOTYPE(int bc_raisemod, (bc_num base, bc_num expo, bc_num mod,
bc_num *result, int scale));
_PROTOTYPE(void bc_raise, (bc_num num1, bc_num num2, bc_num *result,
int scale));
_PROTOTYPE(int bc_sqrt, (bc_num *num, int scale));
_PROTOTYPE(void bc_out_num, (bc_num num, int o_base, void (* out_char)(int),
int leading_zero));
#endif

46
lib/README Normal file
View file

@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

21
platformio.ini Normal file
View file

@ -0,0 +1,21 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
lib_extra_dirs = ~/Documents/Arduino/libraries
lib_deps =
teemuatlut/TMC2130Stepper@^2.5.1
waspinator/AccelStepper@^1.61
; Custom Serial Monitor speed (baud rate)
monitor_speed = 115200

6
src/.theia/launch.json Normal file
View file

@ -0,0 +1,6 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
"version": "0.2.0",
"configurations": []
}

602
src/ATM.ino Normal file
View file

@ -0,0 +1,602 @@
#include <TMC2130Stepper.h>
#include <AccelStepper.h>
#include <MultiStepper.h>
#include <math.h>
#include "ADF4351.h"
#include "Pins.h" // Pins are defined here
#include "Positions.h" // Calibrated frequency positions are defined her
#include "Stepper.h" // Stepper specific values are defined here
#define DEBUG
#include "Debug.h"
// Frequency Settings
#define FREQUENCY_STEP 100000U // 100kHz frequency steps for initial frequency sweep
#define START_FREQUENCY 35000000U // 80MHz
#define STOP_FREQUENCY 200000000 // 120MHz
ADF4351 adf4351(SCLK_PIN, MOSI_PIN, LE_PIN, CE_PIN); // declares object PLL of type ADF4351
TMC2130Stepper tuning_driver = TMC2130Stepper(EN_PIN_M1, DIR_PIN_M1, STEP_PIN_M1, CS_PIN_M1, MOSI_PIN, MISO_PIN, SCLK_PIN);
TMC2130Stepper matching_driver = TMC2130Stepper(EN_PIN_M2, DIR_PIN_M2, STEP_PIN_M2, CS_PIN_M2, MOSI_PIN, MISO_PIN, SCLK_PIN);
AccelStepper tuning_stepper = AccelStepper(tuning_stepper.DRIVER, STEP_PIN_M1, DIR_PIN_M1);
AccelStepper matching_stepper = AccelStepper(matching_stepper.DRIVER, STEP_PIN_M2, DIR_PIN_M2);
Stepper tuner = {tuning_stepper, tuning_driver, DIAG1_PIN_M1, TUNING_STEPPER_HOME};
Stepper matcher = {matching_stepper, matching_driver, DIAG1_PIN_M2, MATCHING_STEPPER_HOME};
void setup() {
Serial.begin(115200);
pinMode(MISO_PIN, INPUT_PULLUP); // Seems to be necessary for SPI to work
tuner.DRIVER.begin(); // Initiate pins and registeries
tuner.DRIVER.rms_current(400); // Set stepper current to 800mA. The command is the same as command TMC2130.setCurrent(600, 0.11, 0.5);
tuner.DRIVER.microsteps(16);
tuner.DRIVER.coolstep_min_speed(0xFFFFF); // 20bit max - needs to be set for stallguard
tuner.DRIVER.diag1_stall(1);
tuner.DRIVER.diag1_active_high(1);
tuner.DRIVER.sg_stall_value(STALL_VALUE - 1);
tuner.DRIVER.shaft_dir(0);
tuner.DRIVER.stealthChop(1); // Enable extremely quiet stepping
pinMode(DIAG1_PIN_M1, INPUT);
Serial.print("DRV_STATUS=0b");
Serial.println(tuning_driver.DRV_STATUS(), BIN);
matcher.DRIVER.begin(); // Initiate pins and registeries
matcher.DRIVER.rms_current(200); // Set stepper current to 200mA. The command is the same as command TMC2130.setCurrent(600, 0.11, 0.5);
matcher.DRIVER.microsteps(16);
matcher.DRIVER.coolstep_min_speed(0xFFFFF); // 20bit max - needs to be set for stallguard
matcher.DRIVER.diag1_stall(1);
matcher.DRIVER.diag1_active_high(1);
matcher.DRIVER.sg_stall_value(STALL_VALUE - 2);
matcher.DRIVER.shaft_dir(0);
matcher.DRIVER.stealthChop(1); // Enable extremely quiet stepping
digitalWrite(EN_PIN_M1, LOW);
digitalWrite(EN_PIN_M2, LOW);
tuner.STEPPER.setMaxSpeed(12000);
tuner.STEPPER.setAcceleration(12000);
tuner.STEPPER.setEnablePin(EN_PIN_M1);
tuner.STEPPER.setPinsInverted(false, false, true);
tuner.STEPPER.enableOutputs();
matcher.STEPPER.setMaxSpeed(12000);
matcher.STEPPER.setAcceleration(12000);
matcher.STEPPER.setEnablePin(EN_PIN_M2);
matcher.STEPPER.setPinsInverted(false, false, true);
matcher.STEPPER.enableOutputs();
tuner.STEPPER.setCurrentPosition(0);
matcher.STEPPER.setCurrentPosition(0);
adf4351.begin();
adf4351.setrf(25000000U);
adf4351.pwrlevel = 3; // This equals -4dBm*/
adf4351.setf(START_FREQUENCY);
}
// Implement Serial communication ...
// This could probably cleaned up by using structs for the inputs, pointing to the different functions- > would reduce copy-paste code and make adding functions more intuitive
void loop() {
if (Serial.available()) {
String input_line = Serial.readStringUntil('\n'); // read string until newline character
char command = input_line.charAt(0); // gets first character of input
// approximate call
// CAREFULL -> if the coil has no proper matching in the frequency range this will not work! Only use this for testing -> otherwise use the automated 'decide' call.
if (command == 'a') {
float target_frequency_MHz = input_line.substring(1).toFloat();
uint32_t target_frequency = validateInput(target_frequency_MHz);
if (target_frequency == 0) return;
Serial.println("Approximating matching to target frequency in MHz:");
Serial.println(target_frequency_MHz);
uint32_t resonance_frequency = findCurrentResonanceFrequency(START_FREQUENCY, STOP_FREQUENCY, FREQUENCY_STEP);
resonance_frequency = approximateResonance(target_frequency, resonance_frequency);
Serial.println("Resonance after approximation is at:");
Serial.println(resonance_frequency);
// bruteforce call
// CAREFULL -> if the current resonance frequency is not within +-5MHz of the target frequency this will not work. Only use this for testing -> otherwise use the automated 'decide' call.
} else if (command == 'b') {
float target_frequency_MHz = input_line.substring(1).toFloat();
uint32_t target_frequency = validateInput(target_frequency_MHz);
if (target_frequency == 0) return;
Serial.println("Bruteforce matching to target frequency in MHz:");
Serial.println(target_frequency_MHz);
uint32_t resonance_frequency = findCurrentResonanceFrequency(START_FREQUENCY, STOP_FREQUENCY, FREQUENCY_STEP);
resonance_frequency = bruteforceResonance(target_frequency, resonance_frequency);
Serial.println("Resonance after bruteforce is at:");
Serial.println(resonance_frequency);
// decide call
// this function decides what kind of t&m mode should be used based on the relationship between target frequency and current resonance
// it also makes sure that there a homing routine performed in case there is currently no proper resonance in the frequency range
} else if (command == 'd'){
float target_frequency_MHz = input_line.substring(1).toFloat();
uint32_t target_frequency = validateInput(target_frequency_MHz);
if (target_frequency == 0) return;
Serial.println("Tuning and Matching to target frequency in MHz (automatic mode):");
Serial.println(target_frequency_MHz);
uint32_t resonance_frequency = automaticTM(target_frequency);
Serial.println("Resonance after tuning and matching is at:");
Serial.println(resonance_frequency);
// home call
// Perform the homing routine by looking for the limit of the capacitors
// it also places the steppers in a way so there is a resonance dip inside the frequency range
// CAREFULL -> The values are hardcoded, these need to be changed if there is a different coil in use
} else if (command == 'h'){
Serial.println("Homing...");
homeStepper(tuner);
homeStepper(matcher);
Serial.println("Resonance frequency after homing:");
uint32_t resonance_frequency = findCurrentResonanceFrequency(START_FREQUENCY, STOP_FREQUENCY, FREQUENCY_STEP);
Serial.println(resonance_frequency);
// frequency sweep call
// scans the frequency range for the current resonance frequency
} else if (command == 'f'){
Serial.println("Frequency sweep...");
uint32_t resonance_frequency = findCurrentResonanceFrequency(START_FREQUENCY, STOP_FREQUENCY, FREQUENCY_STEP);
Serial.println("Resonance is at:");
Serial.println(resonance_frequency);
// calculates Reflection loss for a given frequency
} else if (command == 'r'){
float frequency_MHz = input_line.substring(1).toFloat();
uint32_t frequency = validateInput(frequency_MHz);
if (frequency == 0) return;
float reflection_loss = getReflectionRMS(frequency);
Serial.println("For frequency:");
Serial.println(frequency);
Serial.println("RMS of the reflection is:");
Serial.println(reflection_loss);
//optimize Matching
} else if (command == 'm'){
Serial.println("Optimize Matching around frequency:");
uint32_t resonance_frequency = findCurrentResonanceFrequency(START_FREQUENCY, STOP_FREQUENCY, FREQUENCY_STEP);
Serial.println(resonance_frequency);
optimizeMatching(resonance_frequency);
//calculate sum
} else if (command == 's'){
float frequency_MHz = input_line.substring(1).toFloat();
uint32_t frequency = validateInput(frequency_MHz);
if (frequency == 0) return;
int sum = sumReflectionAroundFrequency(frequency);
Serial.println("For frequency:");
Serial.println(frequency);
Serial.println("Sum of the reflection is:");
Serial.println(sum);
// Calibration Call
} else if (command == 'c'){
Serial.println("Calibrating ...");
getCalibrationValues();
} else {
Serial.println("Invalid Input");
}
}
}
// This helper function checks if the input frequency is plausible, if so it returns the value in Hz
// otherwise it returns 0
uint32_t validateInput(float frequency_MHz){
uint32_t frequency_Hz = frequency_MHz * 1000000U;
if (frequency_Hz < START_FREQUENCY){
Serial.println("Invalid input: frequency too low");
return 0;
} else if (frequency_Hz > STOP_FREQUENCY) {
Serial.println("Invalid input: frequency too high");
return 0;
}else{
return frequency_Hz;
}
}
int readReflection(int averages){
int reflection = 0;
for (int i = 0; i < averages; i ++) reflection += analogReadMilliVolts(REFLECTION_PIN);
return reflection/averages;
}
void getCalibrationValues(){
uint32_t resonance_frequency = findCurrentResonanceFrequency(START_FREQUENCY, STOP_FREQUENCY, FREQUENCY_STEP);
tuner.STEPPER.setCurrentPosition(0);
matcher.STEPPER.setCurrentPosition(0);
int tuner_position = stallStepper(tuner);
int matcher_position = stallStepper(matcher);
Serial.println("For Resonance frequency:");
Serial.println(resonance_frequency);
Serial.println("Tuner Calibration is:");
Serial.println(tuner_position);
Serial.println("Matcher Position is:");
Serial.println(matcher_position);
}
//should put the stepper stuff into a struct
void homeStepper(Stepper stepper){
stallStepper(stepper);
stepper.STEPPER.setCurrentPosition(0);
stepper.STEPPER.moveTo(1000);
stepper.STEPPER.runToPosition();
stepper.STEPPER.setMaxSpeed(3000);
stepper.STEPPER.setAcceleration(3000);
stepper.DRIVER.sg_stall_value(-64); // Stall value needs to be lowered because of slower stepper
stallStepper(stepper);
stepper.DRIVER.sg_stall_value(STALL_VALUE);
stepper.STEPPER.setMaxSpeed(12000);
stepper.STEPPER.setAcceleration(12000);
stepper.STEPPER.setCurrentPosition(0);
stepper.STEPPER.moveTo(stepper.HOME_POSITION);
stepper.STEPPER.runToPosition();
}
int stallStepper(Stepper stepper){
stepper.STEPPER.moveTo(-9999999);
while (!digitalRead(stepper.STALL_PIN)){
stepper.STEPPER.run();
}
DEBUG_PRINT(stepper.STEPPER.currentPosition());
stepper.STEPPER.stop();
return stepper.STEPPER.currentPosition(); // returns value until limit is reached
}
uint32_t automaticTM(uint32_t target_frequency){
uint32_t resonance_frequency = findCurrentResonanceFrequency(START_FREQUENCY, STOP_FREQUENCY, FREQUENCY_STEP);
int32_t delta_frequency = target_frequency - resonance_frequency; // needs to be int -> negative frequencies possible
if (abs(delta_frequency) > 5000000U) resonance_frequency = approximateResonance(target_frequency, resonance_frequency);
resonance_frequency = bruteforceResonance(target_frequency, resonance_frequency);
optimizeMatching(resonance_frequency);
resonance_frequency = findCurrentResonanceFrequency(resonance_frequency - 1000000U, resonance_frequency + 1000000U, FREQUENCY_STEP / 2);
resonance_frequency = bruteforceResonance(target_frequency, resonance_frequency);
return resonance_frequency;
}
// calculates the Reflection Loss at a specified frequency
//24mV/dB slope
//0dBV defined as 1V Sin RMS
// Would expect 1.74V as output for unmatched coil -> but it's 1.65V => ~10mV at Logamp
// Measurments: with 40dB LNA @85MHz
// Open: 1.6V RMS output
// Coil matched to -30dB: 1.0V RmS Output
float calculateRL(uint32_t frequency){
float RMS_ADF = 13; // at -4dBm the ADF4351 generates sin with an RMS value of 131.5mV but due to to -10dB attenuation of the transcoupler and some additional reflections about 13mV are effectivly at the Logamp
float reflection_rms = getReflectionRMS(frequency);
float reflection_loss = 20 * log10((reflection_rms)/ RMS_ADF);
return reflection_loss;
}
float getReflectionRMS(uint32_t frequency){
float LOGAMP_SLOPE = 24; // Slope in mV/dB
adf4351.setf(frequency);
delay(10);
int reflection_mv = readReflection(64); // Output of the logamp
int intercept_positioning = -108; // in dB
float reflection_dBV = (reflection_mv/LOGAMP_SLOPE) + intercept_positioning;
float reflection_rms = pow(10, reflection_dBV / 20) * 1000; // this step could be shortened but I still like to calculate it explicitly since there are multiple logarithmic operations going on here - > this value is in mV
return reflection_rms;
}
// Finds current Resonance Frequency of the coil. There should be a substential dip already present atm.
// Add plausibility check to make sure there is one peak at at least -12dB
// Following is for setup WITHOUT 20dB LNA:
// -30dB aprox. 1.15V Oscilloscope -> normally 1.6V -> 1300 Points
// -16dB aprox. 1.27V Oscilloscope - normally 1.6V
// -18dB aprox 1.295V Oscilloscope -> use 1489 Points as decision line for sufficient Matching
// Values for setup WITH 20dB LNA: -> i don't know what happened here.
// open 1.2V
//-16dB 827mV Oscilloscope
//-30dB 770mV Oscilloscope
int32_t findCurrentResonanceFrequency(uint32_t start_frequency, uint32_t stop_frequency, uint32_t frequency_step){
int minimum_reflection = 4096;
int current_reflection = 0;
uint32_t minimum_frequency = 0;
float reflection_rms = 0;
adf4351.setf(start_frequency); // A frequency value needs to be set once -> there seems to be a bug with the first SPI call
for(uint32_t frequency = start_frequency; frequency <= stop_frequency; frequency += frequency_step){
adf4351.setf(frequency);
delay(5); // This delay is essential! There is a glitch with ADC2 that leads to wrong readings if GPIO27 is set to high for multiple microseconds.
current_reflection = readReflection(4);
if (current_reflection < minimum_reflection){
minimum_frequency = frequency;
minimum_reflection = current_reflection;
}
}
reflection_rms = getReflectionRMS(minimum_frequency);
if (reflection_rms > 10){
Serial.println("Resonance could not be found.");
Serial.println(reflection_rms);
return -1;
}
return minimum_frequency;
}
// Approximates the target frequency to about 3 MHZ with the tuning capacitor .... works so far
int32_t approximateResonance(uint32_t target_frequency, uint32_t current_resonance_frequency){
int32_t delta_frequency = target_frequency - current_resonance_frequency;
int rotation = 0; // rotation == 1 -> clockwise, rotation == -1 -> counterclockwise
if (delta_frequency < 0) rotation = -1; // negative delta means currentresonance is to high, hence anticlockwise movement is necessary
else rotation = 1;
int start_position = tuner.STEPPER.currentPosition();
tuner.STEPPER.move(STEPS_PER_ROTATION * rotation); // This needs to be changed
tuner.STEPPER.runToPosition();
// @ Optimization possibility: -> just scan plausible area, would reduce half the scan time
int32_t one_revolution_resonance = findCurrentResonanceFrequency(current_resonance_frequency - 30000000U, current_resonance_frequency + 30000000U, FREQUENCY_STEP);
DEBUG_PRINT(one_revolution_resonance);
int32_t delta_one_revolution_frequency = one_revolution_resonance - current_resonance_frequency;
//Plausibility Check - prevents the stepper from turning forever.
if ((one_revolution_resonance == -1) || (abs(delta_one_revolution_frequency) > 30000000U)){
Serial.println("Tuning and matching not possible - homing needed.");
return -1;
}
int32_t steps_to_delta_frequency = ((float) delta_frequency / (float) delta_one_revolution_frequency) * STEPS_PER_ROTATION * rotation;
DEBUG_PRINT(delta_one_revolution_frequency);
DEBUG_PRINT(delta_frequency);
DEBUG_PRINT(tuner.STEPPER.currentPosition());
DEBUG_PRINT(steps_to_delta_frequency);
tuner.STEPPER.moveTo(start_position + steps_to_delta_frequency);
tuner.STEPPER.runToPosition();
DEBUG_PRINT(tuner.STEPPER.currentPosition());
current_resonance_frequency = findCurrentResonanceFrequency(target_frequency - 5000000U, target_frequency + 5000000U, FREQUENCY_STEP);
return(current_resonance_frequency);
}
// Tries out different capacitor position until iteration depth is reached OR current_resonancy frequency matches the target_frequency
int32_t bruteforceResonance(uint32_t target_frequency, uint32_t current_resonance_frequency){
// Change Tuning Stepper -> Clockwise => Freq goes up
// Dir = 0 => Anticlockwise movement
int rotation = 0; // rotation == 1 -> clockwise, rotation == -1 -> counterclockwise
int ITERATIONS = 25; // Iteration depth
int iteration_steps = 0;
int32_t delta_frequency = target_frequency - current_resonance_frequency;
if (delta_frequency < 0) rotation = -1; // negative delta means currentresonance is too high, hence anticlockwise movement is necessary
else rotation = 1;
iteration_steps = rotation * (STEPS_PER_ROTATION / 50);
//'bruteforce' the stepper position to match the target frequency
for (int i = 0; i < ITERATIONS; i ++){
tuner.STEPPER.move(iteration_steps);
tuner.STEPPER.runToPosition();
// @ Optimization possibility: Reduce frequency range when close to target_frequency
current_resonance_frequency = findCurrentResonanceFrequency(target_frequency - 5000000U, target_frequency + 5000000U, FREQUENCY_STEP / 2);
//Serial.println(current_resonance_frequency);
//Serial.println(rotation);
//Serial.println(iteration_steps);
// Stops the iteration if the minima matches the target frequency
if (current_resonance_frequency == target_frequency) break;
// This means the bruteforce resolution was too high and the resonance frequency overshoot
// therfore the turn direction gets inverted and the increment halfed
if ((current_resonance_frequency > target_frequency) && (rotation == 1)) {
rotation = -1;
iteration_steps /= 2;
iteration_steps *= rotation;
}
else if ((current_resonance_frequency < target_frequency) && (rotation == -1)){
rotation = 1;
iteration_steps /= 2;
iteration_steps *= -rotation;
}
}
return current_resonance_frequency;
}
//
// Matcher clockwise lowers resonance frequency
int optimizeMatching(uint32_t current_resonance_frequency){
int ITERATIONS = 20; // //100 equals one full rotation
int iteration_steps = 0;
int minimum_reflection = 10e5;
int current_reflection = 0;
int minimum_matching_position = 0;
int last_reflection = 10e5;
int rotation = 1;
// Look which rotation direction improves matching.
rotation = getMatchRotation(current_resonance_frequency);
DEBUG_PRINT(rotation);
// This tries to find the minimum reflection while ignoring the change in resonance -> it always looks for minima within
iteration_steps = rotation * (STEPS_PER_ROTATION / 10);
DEBUG_PRINT(iteration_steps);
adf4351.setf(current_resonance_frequency);
for (int i = 0; i < ITERATIONS; i ++){
//while(minimum_reflection > 270000){
DEBUG_PRINT("Iteration");
current_reflection = 0;
matcher.STEPPER.move(iteration_steps);
matcher.STEPPER.runToPosition();
delay(250);
current_resonance_frequency = findCurrentResonanceFrequency(current_resonance_frequency - 1000000U, current_resonance_frequency + 1000000U, FREQUENCY_STEP);
current_reflection = sumReflectionAroundFrequency(current_resonance_frequency);
if (current_reflection < minimum_reflection){
minimum_matching_position = matcher.STEPPER.currentPosition();
minimum_reflection = current_reflection;
DEBUG_PRINT("Minimum");
DEBUG_PRINT(minimum_matching_position);
}
/*if (current_reflection > last_reflection) {
rotation *= -1;
//iteration_steps /= 2;
iteration_steps *= rotation;
}*/
DEBUG_PRINT(matcher.STEPPER.currentPosition());
DEBUG_PRINT(current_resonance_frequency);
DEBUG_PRINT(last_reflection);
last_reflection = current_reflection;
if(iteration_steps == 0) break;
DEBUG_PRINT(current_reflection);
}
matcher.STEPPER.moveTo(minimum_matching_position);
matcher.STEPPER.runToPosition();
DEBUG_PRINT(matcher.STEPPER.currentPosition());
return (minimum_reflection);
}
// probably do this for multiple positions in each direction
int getMatchRotation(uint32_t current_resonance_frequency){
matcher.STEPPER.move(STEPS_PER_ROTATION / 2);
matcher.STEPPER.runToPosition();
current_resonance_frequency = findCurrentResonanceFrequency(current_resonance_frequency - 1000000U, current_resonance_frequency + 1000000U, FREQUENCY_STEP / 10);
int clockwise_match = sumReflectionAroundFrequency(current_resonance_frequency);
matcher.STEPPER.move(-2* (STEPS_PER_ROTATION / 2));
matcher.STEPPER.runToPosition();
current_resonance_frequency = findCurrentResonanceFrequency(current_resonance_frequency - 1000000U, current_resonance_frequency + 1000000U, FREQUENCY_STEP / 10);
int anticlockwise_match = sumReflectionAroundFrequency(current_resonance_frequency);
matcher.STEPPER.move(STEPS_PER_ROTATION / 2);
matcher.STEPPER.runToPosition();
DEBUG_PRINT(clockwise_match);
DEBUG_PRINT(anticlockwise_match);
if (clockwise_match < anticlockwise_match) return 1;
else return -1;
}
int sumReflectionAroundFrequency(uint32_t center_frequency){
int sum_reflection = 0;
// sum approach -> cummulates reflection around resonance -> reduce influence of wrong minimum and noise
for (uint32_t frequency = center_frequency - 500000U; frequency < center_frequency + 500000U; frequency+= FREQUENCY_STEP / 10) {
adf4351.setf(frequency);
delay(10);
sum_reflection += readReflection(16);
}
return sum_reflection;
}

11
test/README Normal file
View file

@ -0,0 +1,11 @@
This directory is intended for PlatformIO Unit Testing and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/page/plus/unit-testing.html