mirror of
https://github.com/nqrduck/ATM.git
synced 2024-11-22 10:02:26 +00:00
Implemented new command structure.
This commit is contained in:
parent
8c567bbc8d
commit
c4b68ef231
14 changed files with 833 additions and 651 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,3 +4,4 @@
|
|||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
.vscode
|
||||
src/dummy.h
|
||||
|
|
684
src/ATM.ino
684
src/ATM.ino
|
@ -1,18 +1,21 @@
|
|||
#include <TMC2130Stepper.h>
|
||||
#include <AccelStepper.h>
|
||||
#include <MultiStepper.h>
|
||||
#include <math.h>
|
||||
#include "global.h"
|
||||
|
||||
#include "ADF4351.h"
|
||||
#include "AD5593R.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
|
||||
#include "CommandManager.h"
|
||||
#include "commands/FrequencySweep.h"
|
||||
#include "commands/TuneMatch.h"
|
||||
#include "commands/Homing.h"
|
||||
|
||||
#define DEBUG
|
||||
|
||||
#include "Debug.h"
|
||||
|
||||
CommandManager commandManager;
|
||||
|
||||
// Commands
|
||||
FrequencySweep frequencySweep;
|
||||
TuneMatch tuneMatch;
|
||||
Homing homing;
|
||||
|
||||
// Frequency Settings
|
||||
#define FREQUENCY_STEP 100000U // 100kHz frequency steps for initial frequency sweep
|
||||
#define START_FREQUENCY 50000000U // 50MHz
|
||||
|
@ -41,8 +44,15 @@ void setup()
|
|||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
// Here the commands are registered
|
||||
commandManager.registerCommand('f', &frequencySweep);
|
||||
commandManager.registerCommand('d', &tuneMatch);
|
||||
commandManager.registerCommand('h', &homing);
|
||||
|
||||
pinMode(MISO_PIN, INPUT_PULLUP); // Seems to be necessary for SPI to work
|
||||
|
||||
|
||||
// Setup fo the tuning stepper
|
||||
tuner.DRIVER.begin(); // Initiate pins
|
||||
tuner.DRIVER.rms_current(400); // Set stepper current to 400mA.
|
||||
tuner.DRIVER.microsteps(16);
|
||||
|
@ -55,6 +65,7 @@ void setup()
|
|||
|
||||
pinMode(DIAG1_PIN_M1, INPUT);
|
||||
|
||||
// Setup fo the matching stepper
|
||||
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);
|
||||
|
@ -84,12 +95,14 @@ void setup()
|
|||
|
||||
matcher.STEPPER.setCurrentPosition(0);
|
||||
|
||||
// Setup for the ADF4351 frequency synthesizer
|
||||
adf4351.begin();
|
||||
|
||||
adf4351.setrf(25000000U);
|
||||
adf4351.pwrlevel = 2; // This equals -4dBm*/ For the electrical probe coils one should use at least -20dbm so an attenuator is necessary
|
||||
adf4351.setf(START_FREQUENCY);
|
||||
|
||||
// Setup for the RF Switch for the filterbank
|
||||
pinMode(FILTER_SWITCH_A, OUTPUT);
|
||||
pinMode(FILTER_SWITCH_B, OUTPUT);
|
||||
|
||||
|
@ -104,13 +117,17 @@ void setup()
|
|||
|
||||
adac.configure_ADCs(ADCs);
|
||||
|
||||
// RF Switch
|
||||
// RF Switch for switching between preamp and tuning and matching module
|
||||
pinMode(RF_SWITCH_PIN, OUTPUT);
|
||||
digitalWrite(RF_SWITCH_PIN, HIGH);
|
||||
}
|
||||
|
||||
// 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
|
||||
// Serial communication via USB.
|
||||
// Commands:
|
||||
// f<start frequency>f<stop frequency>f<frequency step> - Frequency Sweep
|
||||
// d<target frequency in MHz>f<start frequency>f<stop frequency>f<frequency step> - Tune and Match
|
||||
// h - Homing
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (Serial.available())
|
||||
|
@ -120,95 +137,11 @@ void loop()
|
|||
|
||||
char command = input_line.charAt(0); // gets first character of input
|
||||
|
||||
commandManager.executeCommand(command, input_line);
|
||||
commandManager.printCommandResult(command);
|
||||
// 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')
|
||||
{
|
||||
printInfo("Not implemented");
|
||||
|
||||
// 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;
|
||||
|
||||
printInfo("Bruteforce matching to target frequency in MHz:");
|
||||
printInfo(target_frequency_MHz);
|
||||
|
||||
uint32_t resonance_frequency = findCurrentResonanceFrequency(START_FREQUENCY, STOP_FREQUENCY, FREQUENCY_STEP);
|
||||
|
||||
resonance_frequency = bruteforceResonance(target_frequency, resonance_frequency);
|
||||
printInfo("Resonance after bruteforce is at:");
|
||||
printInfo(String(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;
|
||||
|
||||
printInfo("Tuning and Matching to target frequency in MHz (automatic mode):");
|
||||
printInfo(target_frequency_MHz);
|
||||
|
||||
uint32_t resonance_frequency = automaticTM(target_frequency);
|
||||
|
||||
printInfo("Resonance after tuning and matching is at:");
|
||||
printInfo(resonance_frequency);
|
||||
|
||||
printInfo("Matched to RL in dB:");
|
||||
printInfo(calculateRL(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')
|
||||
{
|
||||
printInfo("Homing...");
|
||||
tuner.STEPPER.setCurrentPosition(homeStepper(tuner));
|
||||
matcher.STEPPER.setCurrentPosition(homeStepper(matcher));
|
||||
homed = true;
|
||||
|
||||
printInfo("Resonance frequency after homing:");
|
||||
uint32_t resonance_frequency = findCurrentResonanceFrequency(START_FREQUENCY, STOP_FREQUENCY, FREQUENCY_STEP / 2);
|
||||
printInfo(resonance_frequency);
|
||||
|
||||
// frequency sweep call
|
||||
// scans the frequency range for the current resonance frequency
|
||||
}
|
||||
else if (command == 'f')
|
||||
{
|
||||
printInfo("Started frequency sweep");
|
||||
// Get the start frequency which is the value until the next f character
|
||||
char delimiter = 'f';
|
||||
// Indices for each variable
|
||||
int startFreqIndex = input_line.indexOf(delimiter) + 1;
|
||||
int stopFreqIndex = input_line.indexOf(delimiter, startFreqIndex) + 1;
|
||||
int freqStepIndex = input_line.indexOf(delimiter, stopFreqIndex) + 1;
|
||||
|
||||
// Extract each variable from the string
|
||||
uint32_t startFreq = input_line.substring(startFreqIndex, stopFreqIndex - 1).toInt(); // Subtract 1 to not include the delimiter
|
||||
uint32_t stopFreq = input_line.substring(stopFreqIndex, freqStepIndex - 1).toInt();
|
||||
uint32_t freqStep = input_line.substring(freqStepIndex).toInt(); // If no second parameter is provided, substring() goes to the end of the string
|
||||
|
||||
uint32_t resonance_frequency = findCurrentResonanceFrequency(startFreq, stopFreq, freqStep);
|
||||
|
||||
// This tells the PC that the frequency sweep is finished
|
||||
Serial.print("r");
|
||||
printInfo(resonance_frequency);
|
||||
|
||||
// calculates Reflection loss for a given frequency
|
||||
}
|
||||
/*if (command == 'a')
|
||||
else if (command == 'r')
|
||||
{
|
||||
float frequency_MHz = input_line.substring(1).toFloat();
|
||||
|
@ -260,557 +193,6 @@ void loop()
|
|||
else
|
||||
{
|
||||
printInfo("Invalid Input");
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function checks if the input is valid. It checks if the frequency is within the allowed range.
|
||||
*
|
||||
* @param frequency_MHz The frequency that should be checked in MHz.
|
||||
* @return uint32_t The frequency in Hz if the input is valid, 0 otherwise.
|
||||
*
|
||||
* @example validateInput(100); // returns 100000000U
|
||||
*/
|
||||
uint32_t validateInput(float frequency_MHz)
|
||||
{
|
||||
uint32_t frequency_Hz = frequency_MHz * 1000000U;
|
||||
|
||||
if (frequency_Hz < START_FREQUENCY)
|
||||
{
|
||||
printError("Invalid input: frequency too low");
|
||||
return 0;
|
||||
}
|
||||
else if (frequency_Hz > 300000000U)
|
||||
{
|
||||
printError("Invalid input: frequency too high");
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return frequency_Hz;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function reads the reflection at the current frequency. It does not set the frequency.
|
||||
*
|
||||
* @param averages The number of readings that should be averaged
|
||||
* @return int The average reflection in millivolts
|
||||
*
|
||||
* @example readReflection(64); // reads the reflection at the current frequency and averages over 64 readings
|
||||
*/
|
||||
int readReflection(int averages)
|
||||
{
|
||||
int reflection = 0;
|
||||
for (int i = 0; i < averages; i++)
|
||||
// We multiply by 1000 to get the result in millivolts
|
||||
reflection += (adac.read_ADC(0) * 1000);
|
||||
return reflection / averages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function reads the phase at the current frequency. It does not set the frequency.
|
||||
*
|
||||
* @param averages The number of readings that should be averaged
|
||||
* @return int The average phase in millivolts
|
||||
*
|
||||
* @example readPhase(64); // reads the phase at the current frequency and averages over 64 readings
|
||||
*/
|
||||
int readPhase(int averages)
|
||||
{
|
||||
int phase = 0;
|
||||
for (int i = 0; i < averages; i++)
|
||||
phase += (adac.read_ADC(1) * 1000);
|
||||
return phase / averages;
|
||||
}
|
||||
/**
|
||||
* @brief This function should be called after manually tuning and matching the probe coil. It will then print the stepper positions for the current resonance frequency.
|
||||
* This can be used to calibrate the stepper positions for the different frequency ranges.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @example getCalibrationValues(); // prints the stepper positions for the current resonance frequency
|
||||
*/
|
||||
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);
|
||||
|
||||
printInfo("For Resonance frequency:");
|
||||
printInfo(resonance_frequency);
|
||||
printInfo("Tuner Calibration is:");
|
||||
printInfo(tuner_position);
|
||||
printInfo("Matcher Position is:");
|
||||
printInfo(matcher_position);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function performs a homing of the steppers. It returns the current position of the stepper.
|
||||
*
|
||||
* @param stepper The stepper that should be homed
|
||||
* @return long The current position of the stepper
|
||||
*
|
||||
* @example homeStepper(tuner); // homes the tuner stepper and returns the current position
|
||||
*/
|
||||
long 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(1000);
|
||||
|
||||
stepper.STEPPER.runToPosition();
|
||||
|
||||
DEBUG_PRINT(stepper.STEPPER.currentPosition());
|
||||
|
||||
return stepper.STEPPER.currentPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function controls the stepper so that they hit the limit and stall. It then returns the current position of the stepper.
|
||||
*
|
||||
* @param stepper The stepper that should be stalled
|
||||
* @return int The current position of the stepper
|
||||
*
|
||||
* @example stallStepper(tuner); // stalls the tuner stepper and returns the current position
|
||||
*/
|
||||
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);
|
||||
|
||||
DEBUG_PRINT("Resonance Frequency before TM");
|
||||
DEBUG_PRINT(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
|
||||
float calculateRL(uint32_t frequency)
|
||||
{
|
||||
setFrequency(frequency);
|
||||
|
||||
float reflection = readReflection(64);
|
||||
|
||||
float reflection_loss = reflection / 6; // Divide by the amplifier gain
|
||||
reflection_loss = reflection_loss / 24; // Divide by the logamp slope
|
||||
|
||||
return reflection_loss;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function finds the current resonance frequency of the coil. There should be a substential dip already present atm.
|
||||
* It also returns the data of the frequency scan which can then be sent to the PC for plotting.
|
||||
*
|
||||
* @param start_frequency The frequency at which the search should start
|
||||
* @param stop_frequency The frequency at which the search should stop
|
||||
* @param frequency_step The frequency step size
|
||||
* @return int32_t The current resonance frequency
|
||||
*
|
||||
* @example findCurrentResonanceFrequency(START_FREQUENCY, STOP_FREQUENCY, FREQUENCY_STEP); // finds the current resonance frequency
|
||||
*/
|
||||
int32_t findCurrentResonanceFrequency(uint32_t start_frequency, uint32_t stop_frequency, uint32_t frequency_step)
|
||||
{
|
||||
int maximum_reflection = 0;
|
||||
int current_reflection = 0;
|
||||
int current_phase = 0;
|
||||
uint32_t minimum_frequency = 0;
|
||||
float reflection = 0;
|
||||
|
||||
adf4351.setf(start_frequency); // A frequency value needs to be set once -> there seems to be a bug with the first SPI call
|
||||
delay(50);
|
||||
|
||||
for (uint32_t frequency = start_frequency; frequency <= stop_frequency; frequency += frequency_step)
|
||||
{
|
||||
//adf4351.setf(frequency);
|
||||
setFrequency(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);
|
||||
current_phase = readPhase(4);
|
||||
|
||||
// Send out the frequency identifier f with the frequency value
|
||||
Serial.println(String("f") + frequency + "r" + current_reflection + "p" + current_phase);
|
||||
|
||||
if (current_reflection > maximum_reflection)
|
||||
{
|
||||
minimum_frequency = frequency;
|
||||
maximum_reflection = current_reflection;
|
||||
}
|
||||
}
|
||||
|
||||
adf4351.setf(minimum_frequency);
|
||||
delay(50);
|
||||
reflection = readReflection(16);
|
||||
if (reflection < 130)
|
||||
{
|
||||
DEBUG_PRINT("Resonance could not be found.");
|
||||
DEBUG_PRINT(reflection);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Capacitor needs to charge - therefore rerun around area with longer delay. -> REFACTOR THIS!!!!
|
||||
maximum_reflection = 0;
|
||||
for (uint32_t frequency = minimum_frequency - 300000U; frequency <= minimum_frequency + 300000U; frequency += frequency_step)
|
||||
{
|
||||
adf4351.setf(frequency);
|
||||
delay(100); // Higher delay so the capacitor has time to charge
|
||||
|
||||
current_reflection = readReflection(64);
|
||||
|
||||
if (current_reflection > maximum_reflection)
|
||||
{
|
||||
minimum_frequency = frequency;
|
||||
maximum_reflection = current_reflection;
|
||||
}
|
||||
}
|
||||
|
||||
return minimum_frequency;
|
||||
}
|
||||
/**
|
||||
* @brief This function sets the frequency of the frequency synthesizer and switches the filterbank accordingly.
|
||||
*
|
||||
* @param frequency The frequency that should be set
|
||||
* @return void
|
||||
*
|
||||
* @example setFrequency(100000000U); // sets the frequency to 100MHz
|
||||
*/
|
||||
void setFrequency(uint32_t frequency)
|
||||
{
|
||||
// First we check what filter has to be used from the FILTERS array
|
||||
// Then we set the filterbank accordingly
|
||||
for (int i = 0; i < sizeof(FILTERS) / sizeof(FILTERS[0]); i++)
|
||||
{
|
||||
// For the first filter we just check if the frequency is below the fg
|
||||
if ((i == 0) && (frequency < FILTERS[i].fg))
|
||||
{
|
||||
digitalWrite(FILTER_SWITCH_A, FILTERS[i].control_input_a);
|
||||
digitalWrite(FILTER_SWITCH_B, FILTERS[i].control_input_b);
|
||||
break;
|
||||
}
|
||||
// For the last filter we just check if the frequency is above the fg
|
||||
else if ((i == sizeof(FILTERS) / sizeof(FILTERS[0]) - 1) && (frequency > FILTERS[i].fg))
|
||||
{
|
||||
digitalWrite(FILTER_SWITCH_A, FILTERS[i].control_input_a);
|
||||
digitalWrite(FILTER_SWITCH_B, FILTERS[i].control_input_b);
|
||||
break;
|
||||
}
|
||||
// For the filters in between we check if the frequency is between the fg and the fg of the previous filter
|
||||
else if ((frequency < FILTERS[i].fg) && (frequency > FILTERS[i - 1].fg))
|
||||
{
|
||||
digitalWrite(FILTER_SWITCH_A, FILTERS[i].control_input_a);
|
||||
digitalWrite(FILTER_SWITCH_B, FILTERS[i].control_input_b);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Finally we set the frequency
|
||||
adf4351.setf(frequency);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function tries out different capacitor positions until iteration depth is reached OR current_resonancy frequency matches the target_frequency.
|
||||
*
|
||||
* @param target_frequency The frequency that should be matched
|
||||
* @param current_resonance_frequency The current resonance frequency
|
||||
* @return int32_t The current resonance frequency
|
||||
*
|
||||
* @example bruteforceResonance(100000000U, 90000000U); // tries to match 100MHz with a current resonance frequency of 90MHz
|
||||
*/
|
||||
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;
|
||||
|
||||
float MATCHING_THRESHOLD = 140; // if the reflection at the current resonance frequency is lower than this threshold re-matching is necessary -> calibrate to ~RL-8dB
|
||||
float resonance_reflection = 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;
|
||||
|
||||
int iteration_start = rotation * (STEPS_PER_ROTATION / 20);
|
||||
|
||||
iteration_steps = iteration_start;
|
||||
DEBUG_PRINT(iteration_start);
|
||||
|
||||
//'bruteforce' the stepper position to match the target frequency
|
||||
|
||||
for (int i = 0; i < ITERATIONS; i++)
|
||||
{
|
||||
tuner.STEPPER.move(iteration_steps);
|
||||
tuner.STEPPER.runToPosition();
|
||||
|
||||
// Only rematch matcher for large step width
|
||||
if (iteration_steps == iteration_start)
|
||||
{
|
||||
matcher.STEPPER.move(-iteration_steps * 3);
|
||||
matcher.STEPPER.runToPosition();
|
||||
}
|
||||
|
||||
// @ Optimization possibility: Reduce frequency range when close to target_frequency
|
||||
current_resonance_frequency = findCurrentResonanceFrequency(current_resonance_frequency - 5000000U, current_resonance_frequency + 5000000U, FREQUENCY_STEP / 2);
|
||||
|
||||
DEBUG_PRINT(current_resonance_frequency);
|
||||
|
||||
// Stops the iteration if the minima matches the target frequency
|
||||
if (current_resonance_frequency == target_frequency)
|
||||
break;
|
||||
|
||||
adf4351.setf(current_resonance_frequency);
|
||||
delay(100);
|
||||
resonance_reflection = readReflection(16);
|
||||
DEBUG_PRINT(resonance_reflection);
|
||||
|
||||
if (resonance_reflection < MATCHING_THRESHOLD)
|
||||
{
|
||||
optimizeMatching(current_resonance_frequency);
|
||||
}
|
||||
|
||||
// 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 = 50;
|
||||
int iteration_steps = 0;
|
||||
|
||||
int maximum_reflection = 0;
|
||||
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 / 20);
|
||||
|
||||
DEBUG_PRINT(iteration_steps);
|
||||
|
||||
adf4351.setf(current_resonance_frequency);
|
||||
for (int i = 0; i < ITERATIONS; i++)
|
||||
{
|
||||
DEBUG_PRINT(i);
|
||||
current_reflection = 0;
|
||||
|
||||
matcher.STEPPER.move(iteration_steps);
|
||||
matcher.STEPPER.runToPosition();
|
||||
|
||||
delay(50);
|
||||
|
||||
current_resonance_frequency = findCurrentResonanceFrequency(current_resonance_frequency - 1000000U, current_resonance_frequency + 1000000U, FREQUENCY_STEP / 2);
|
||||
|
||||
// Skip this iteration if the resonance has been lost
|
||||
if (current_resonance_frequency == 0)
|
||||
{
|
||||
delay(1000); // Wait for one second since something has gone wrong
|
||||
continue;
|
||||
}
|
||||
|
||||
adf4351.setf(current_resonance_frequency);
|
||||
delay(100);
|
||||
|
||||
current_reflection = readReflection(16);
|
||||
// current_reflection = sumReflectionAroundFrequency(current_resonance_frequency);
|
||||
|
||||
if (current_reflection > maximum_reflection)
|
||||
{
|
||||
minimum_matching_position = matcher.STEPPER.currentPosition();
|
||||
maximum_reflection = current_reflection;
|
||||
DEBUG_PRINT("Maximum");
|
||||
DEBUG_PRINT(minimum_matching_position);
|
||||
}
|
||||
|
||||
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 (maximum_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);
|
||||
if (current_resonance_frequency != 0)
|
||||
adf4351.setf(current_resonance_frequency);
|
||||
delay(100);
|
||||
int clockwise_match = readReflection(64);
|
||||
|
||||
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);
|
||||
adf4351.setf(current_resonance_frequency);
|
||||
delay(100);
|
||||
int anticlockwise_match = readReflection(64);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief This function sums up the reflection around a given frequency.
|
||||
*
|
||||
* @param center_frequency The frequency around which the reflection should be summed up
|
||||
* @return int The sum of the reflection around the given frequency
|
||||
*
|
||||
* @example sumReflectionAroundFrequency(100000000U);
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This method should be called when one wants to print information to the serial port.
|
||||
*
|
||||
* @param text The text that should be printed to the serial monitor. It should be a string
|
||||
* @return void
|
||||
*
|
||||
* @example printInfo("This is a test"); // prints "iThis is a test"
|
||||
*/
|
||||
void printInfo(String text) {
|
||||
Serial.println("i" + text);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This method should be called when one wants to print information to the serial port.
|
||||
*
|
||||
* @param number The number that should be printed to the serial monitor. It should be a number
|
||||
* @return void
|
||||
*
|
||||
* @example printInfo(123U); // prints "i123"
|
||||
*/
|
||||
void printInfo(uint32_t number) {
|
||||
Serial.println("i" + String(number)); // convert the number to a string before concatenating
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This method should be called when one wants to an error to the serial port.
|
||||
*
|
||||
* @param text The text that should be printed to the serial monitor. It should be a string
|
||||
* @return void
|
||||
*
|
||||
* @example printError("Duck not found"); // prints "eDuck not found"
|
||||
*/
|
||||
void printError(String text) {
|
||||
Serial.println("e" + text);
|
||||
}
|
||||
|
|
25
src/CommandManager.cpp
Normal file
25
src/CommandManager.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include "CommandManager.h"
|
||||
|
||||
void CommandManager::registerCommand(char identifier, Command* command) {
|
||||
commandMap[identifier] = command;
|
||||
}
|
||||
|
||||
void CommandManager::executeCommand(char identifier, String input_line) {
|
||||
auto it = commandMap.find(identifier);
|
||||
if (it != commandMap.end()) {
|
||||
Command* command = it->second;
|
||||
command->execute(input_line);
|
||||
} else {
|
||||
Serial.println("Unknown command.");
|
||||
}
|
||||
}
|
||||
|
||||
void CommandManager::printCommandResult(char identifier) {
|
||||
auto it = commandMap.find(identifier);
|
||||
if (it != commandMap.end()) {
|
||||
Command* command = it->second;
|
||||
command->printResult();
|
||||
} else {
|
||||
Serial.println("Unknown command.");
|
||||
}
|
||||
}
|
18
src/CommandManager.h
Normal file
18
src/CommandManager.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef COMMANDMANAGER_H
|
||||
#define COMMANDMANAGER_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <map>
|
||||
#include "commands/Command.h"
|
||||
|
||||
class CommandManager {
|
||||
public:
|
||||
void registerCommand(char identifier, Command* command);
|
||||
void executeCommand(char identifier, String input_line);
|
||||
void printCommandResult(char identifier);
|
||||
|
||||
private:
|
||||
std::map<char, Command*> commandMap;
|
||||
};
|
||||
|
||||
#endif
|
397
src/Utilities.cpp
Normal file
397
src/Utilities.cpp
Normal file
|
@ -0,0 +1,397 @@
|
|||
#include <TMC2130Stepper.h>
|
||||
#include <AccelStepper.h>
|
||||
#include <MultiStepper.h>
|
||||
|
||||
|
||||
#include "Utilities.h"
|
||||
|
||||
// Frequency Settings
|
||||
#define FREQUENCY_STEP 100000U // 100kHz frequency steps for initial frequency sweep
|
||||
#define START_FREQUENCY 50000000U // 50MHz
|
||||
#define STOP_FREQUENCY 110000000 // 110MHz
|
||||
|
||||
int32_t findCurrentResonanceFrequency(uint32_t start_frequency, uint32_t stop_frequency, uint32_t frequency_step)
|
||||
{
|
||||
int maximum_reflection = 0;
|
||||
int current_reflection = 0;
|
||||
int current_phase = 0;
|
||||
uint32_t minimum_frequency = 0;
|
||||
float reflection = 0;
|
||||
|
||||
adf4351.setf(start_frequency); // A frequency value needs to be set once -> there seems to be a bug with the first SPI call
|
||||
delay(50);
|
||||
|
||||
for (uint32_t frequency = start_frequency; frequency <= stop_frequency; frequency += frequency_step)
|
||||
{
|
||||
//adf4351.setf(frequency);
|
||||
setFrequency(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);
|
||||
current_phase = readPhase(4);
|
||||
|
||||
// Send out the frequency identifier f with the frequency value
|
||||
Serial.println(String("f") + frequency + "r" + current_reflection + "p" + current_phase);
|
||||
|
||||
if (current_reflection > maximum_reflection)
|
||||
{
|
||||
minimum_frequency = frequency;
|
||||
maximum_reflection = current_reflection;
|
||||
}
|
||||
}
|
||||
|
||||
adf4351.setf(minimum_frequency);
|
||||
delay(50);
|
||||
reflection = readReflection(16);
|
||||
if (reflection < 130)
|
||||
{
|
||||
DEBUG_PRINT("Resonance could not be found.");
|
||||
DEBUG_PRINT(reflection);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Capacitor needs to charge - therefore rerun around area with longer delay. -> REFACTOR THIS!!!!
|
||||
maximum_reflection = 0;
|
||||
for (uint32_t frequency = minimum_frequency - 300000U; frequency <= minimum_frequency + 300000U; frequency += frequency_step)
|
||||
{
|
||||
adf4351.setf(frequency);
|
||||
delay(100); // Higher delay so the capacitor has time to charge
|
||||
|
||||
current_reflection = readReflection(64);
|
||||
|
||||
if (current_reflection > maximum_reflection)
|
||||
{
|
||||
minimum_frequency = frequency;
|
||||
maximum_reflection = current_reflection;
|
||||
}
|
||||
}
|
||||
|
||||
return minimum_frequency;
|
||||
}
|
||||
|
||||
void setFrequency(uint32_t frequency)
|
||||
{
|
||||
// First we check what filter has to be used from the FILTERS array
|
||||
// Then we set the filterbank accordingly
|
||||
for (int i = 0; i < sizeof(FILTERS) / sizeof(FILTERS[0]); i++)
|
||||
{
|
||||
// For the first filter we just check if the frequency is below the fg
|
||||
if ((i == 0) && (frequency < FILTERS[i].fg))
|
||||
{
|
||||
digitalWrite(FILTER_SWITCH_A, FILTERS[i].control_input_a);
|
||||
digitalWrite(FILTER_SWITCH_B, FILTERS[i].control_input_b);
|
||||
break;
|
||||
}
|
||||
// For the last filter we just check if the frequency is above the fg
|
||||
else if ((i == sizeof(FILTERS) / sizeof(FILTERS[0]) - 1) && (frequency > FILTERS[i].fg))
|
||||
{
|
||||
digitalWrite(FILTER_SWITCH_A, FILTERS[i].control_input_a);
|
||||
digitalWrite(FILTER_SWITCH_B, FILTERS[i].control_input_b);
|
||||
break;
|
||||
}
|
||||
// For the filters in between we check if the frequency is between the fg and the fg of the previous filter
|
||||
else if ((frequency < FILTERS[i].fg) && (frequency > FILTERS[i - 1].fg))
|
||||
{
|
||||
digitalWrite(FILTER_SWITCH_A, FILTERS[i].control_input_a);
|
||||
digitalWrite(FILTER_SWITCH_B, FILTERS[i].control_input_b);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Finally we set the frequency
|
||||
adf4351.setf(frequency);
|
||||
|
||||
}
|
||||
|
||||
int readReflection(int averages)
|
||||
{
|
||||
int reflection = 0;
|
||||
for (int i = 0; i < averages; i++)
|
||||
// We multiply by 1000 to get the result in millivolts
|
||||
reflection += (adac.read_ADC(0) * 1000);
|
||||
return reflection / averages;
|
||||
}
|
||||
|
||||
int readPhase(int averages)
|
||||
{
|
||||
int phase = 0;
|
||||
for (int i = 0; i < averages; i++)
|
||||
phase += (adac.read_ADC(1) * 1000);
|
||||
return phase / averages;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
float MATCHING_THRESHOLD = 140; // if the reflection at the current resonance frequency is lower than this threshold re-matching is necessary -> calibrate to ~RL-8dB
|
||||
float resonance_reflection = 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;
|
||||
|
||||
int iteration_start = rotation * (STEPS_PER_ROTATION / 20);
|
||||
|
||||
iteration_steps = iteration_start;
|
||||
DEBUG_PRINT(iteration_start);
|
||||
|
||||
//'bruteforce' the stepper position to match the target frequency
|
||||
|
||||
for (int i = 0; i < ITERATIONS; i++)
|
||||
{
|
||||
tuner.STEPPER.move(iteration_steps);
|
||||
tuner.STEPPER.runToPosition();
|
||||
|
||||
// Only rematch matcher for large step width
|
||||
if (iteration_steps == iteration_start)
|
||||
{
|
||||
matcher.STEPPER.move(-iteration_steps * 3);
|
||||
matcher.STEPPER.runToPosition();
|
||||
}
|
||||
|
||||
// @ Optimization possibility: Reduce frequency range when close to target_frequency
|
||||
current_resonance_frequency = findCurrentResonanceFrequency(current_resonance_frequency - 5000000U, current_resonance_frequency + 5000000U, FREQUENCY_STEP / 2);
|
||||
|
||||
DEBUG_PRINT(current_resonance_frequency);
|
||||
|
||||
// Stops the iteration if the minima matches the target frequency
|
||||
if (current_resonance_frequency == target_frequency)
|
||||
break;
|
||||
|
||||
adf4351.setf(current_resonance_frequency);
|
||||
delay(100);
|
||||
resonance_reflection = readReflection(16);
|
||||
DEBUG_PRINT(resonance_reflection);
|
||||
|
||||
if (resonance_reflection < MATCHING_THRESHOLD)
|
||||
{
|
||||
optimizeMatching(current_resonance_frequency);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
int optimizeMatching(uint32_t current_resonance_frequency)
|
||||
{
|
||||
int ITERATIONS = 50;
|
||||
int iteration_steps = 0;
|
||||
|
||||
int maximum_reflection = 0;
|
||||
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 / 20);
|
||||
|
||||
DEBUG_PRINT(iteration_steps);
|
||||
|
||||
adf4351.setf(current_resonance_frequency);
|
||||
for (int i = 0; i < ITERATIONS; i++)
|
||||
{
|
||||
DEBUG_PRINT(i);
|
||||
current_reflection = 0;
|
||||
|
||||
matcher.STEPPER.move(iteration_steps);
|
||||
matcher.STEPPER.runToPosition();
|
||||
|
||||
delay(50);
|
||||
|
||||
current_resonance_frequency = findCurrentResonanceFrequency(current_resonance_frequency - 1000000U, current_resonance_frequency + 1000000U, FREQUENCY_STEP / 2);
|
||||
|
||||
// Skip this iteration if the resonance has been lost
|
||||
if (current_resonance_frequency == 0)
|
||||
{
|
||||
delay(1000); // Wait for one second since something has gone wrong
|
||||
continue;
|
||||
}
|
||||
|
||||
adf4351.setf(current_resonance_frequency);
|
||||
delay(100);
|
||||
|
||||
current_reflection = readReflection(16);
|
||||
// current_reflection = sumReflectionAroundFrequency(current_resonance_frequency);
|
||||
|
||||
if (current_reflection > maximum_reflection)
|
||||
{
|
||||
minimum_matching_position = matcher.STEPPER.currentPosition();
|
||||
maximum_reflection = current_reflection;
|
||||
DEBUG_PRINT("Maximum");
|
||||
DEBUG_PRINT(minimum_matching_position);
|
||||
}
|
||||
|
||||
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 (maximum_reflection);
|
||||
}
|
||||
|
||||
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);
|
||||
if (current_resonance_frequency != 0)
|
||||
adf4351.setf(current_resonance_frequency);
|
||||
delay(100);
|
||||
int clockwise_match = readReflection(64);
|
||||
|
||||
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);
|
||||
adf4351.setf(current_resonance_frequency);
|
||||
delay(100);
|
||||
int anticlockwise_match = readReflection(64);
|
||||
|
||||
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 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
|
||||
}
|
||||
|
||||
long 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(1000);
|
||||
|
||||
stepper.STEPPER.runToPosition();
|
||||
|
||||
DEBUG_PRINT(stepper.STEPPER.currentPosition());
|
||||
|
||||
return stepper.STEPPER.currentPosition();
|
||||
}
|
||||
|
||||
uint32_t validateInput(float frequency_MHz)
|
||||
{
|
||||
uint32_t frequency_Hz = frequency_MHz * 1000000U;
|
||||
|
||||
if (frequency_Hz < START_FREQUENCY)
|
||||
{
|
||||
printError("Invalid input: frequency too low");
|
||||
return 0;
|
||||
}
|
||||
else if (frequency_Hz > 300000000U)
|
||||
{
|
||||
printError("Invalid input: frequency too high");
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return frequency_Hz;
|
||||
}
|
||||
}
|
||||
|
||||
void printInfo(String text) {
|
||||
Serial.println("i" + text);
|
||||
}
|
||||
|
||||
void printInfo(uint32_t number) {
|
||||
Serial.println("i" + String(number)); // convert the number to a string before concatenating
|
||||
}
|
||||
|
||||
void printError(String text) {
|
||||
Serial.println("e" + text);
|
||||
}
|
148
src/Utilities.h
Normal file
148
src/Utilities.h
Normal file
|
@ -0,0 +1,148 @@
|
|||
#include <math.h>
|
||||
#include <Arduino.h>
|
||||
#include "Debug.h"
|
||||
#include "global.h"
|
||||
|
||||
/**
|
||||
* @brief This function finds the current resonance frequency of the coil. There should be a resonance already present or the algorithm might return nonsense.
|
||||
* It also returns the data of the frequency scan which can then be sent to the PC for plotting.
|
||||
*
|
||||
* @param start_frequency The frequency at which the search should start
|
||||
* @param stop_frequency The frequency at which the search should stop
|
||||
* @param frequency_step The frequency step size
|
||||
* @return int32_t The current resonance frequency
|
||||
*
|
||||
* @example findCurrentResonanceFrequency(START_FREQUENCY, STOP_FREQUENCY, FREQUENCY_STEP); // finds the current resonance frequency
|
||||
*/
|
||||
int32_t findCurrentResonanceFrequency(uint32_t start_frequency, uint32_t stop_frequency, uint32_t frequency_step);
|
||||
|
||||
/**
|
||||
* @brief This function sets the frequency of the frequency synthesizer and switches the filterbank accordingly.
|
||||
*
|
||||
* @param frequency The frequency that should be set
|
||||
* @return void
|
||||
*
|
||||
* @example setFrequency(100000000U); // sets the frequency to 100MHz
|
||||
*/
|
||||
void setFrequency(uint32_t frequency);
|
||||
|
||||
/**
|
||||
* @brief This function reads the reflection at the current frequency. It does not set the frequency.
|
||||
*
|
||||
* @param averages The number of readings that should be averaged
|
||||
* @return int The average reflection in millivolts
|
||||
*
|
||||
* @example readReflection(64); // reads the reflection at the current frequency and averages over 64 readings
|
||||
*/
|
||||
int readReflection(int averages);
|
||||
|
||||
/**
|
||||
* @brief This function reads the phase at the current frequency. It does not set the frequency.
|
||||
*
|
||||
* @param averages The number of readings that should be averaged
|
||||
* @return int The average phase in millivolts
|
||||
*
|
||||
* @example readPhase(64); // reads the phase at the current frequency and averages over 64 readings
|
||||
*/
|
||||
int readPhase(int averages);
|
||||
|
||||
/**
|
||||
* @brief This function sums up the reflection around a given frequency.
|
||||
*
|
||||
* @param center_frequency The frequency around which the reflection should be summed up
|
||||
* @return int The sum of the reflection around the given frequency
|
||||
*
|
||||
* @example sumReflectionAroundFrequency(100000000U);
|
||||
*/
|
||||
int sumReflectionAroundFrequency(uint32_t center_frequency);
|
||||
|
||||
/**
|
||||
* @brief This function tries out different capacitor positions until iteration depth is reached OR current_resonancy frequency matches the target_frequency.
|
||||
*
|
||||
* @param target_frequency The frequency that should be matched
|
||||
* @param current_resonance_frequency The current resonance frequency
|
||||
* @return int32_t The current resonance frequency
|
||||
*
|
||||
* @example bruteforceResonance(100000000U, 90000000U); // tries to match 100MHz with a current resonance frequency of 90MHz
|
||||
*/
|
||||
int32_t bruteforceResonance(uint32_t target_frequency, uint32_t current_resonance_frequency);
|
||||
|
||||
/**
|
||||
* @brief This function tries to find a matching capacitor position that will decrease the reflection at the current resonance frequency to a minimum.
|
||||
* It will then move the stepper to this position.
|
||||
*
|
||||
* @param current_resonance_frequency The current resonance frequency
|
||||
* @return int The reflection at the minimum matching position
|
||||
*
|
||||
* @example optimizeMatching(100000000); // tries to find the minimum reflection at 100 MHz and moves the stepper to this position
|
||||
*/
|
||||
int optimizeMatching(uint32_t current_resonance_frequency);
|
||||
|
||||
/**
|
||||
* @brief This function finds the direction which the matching capacitor should be turned to decrease the reflection.
|
||||
*
|
||||
* @param current_resonance_frequency The current resonance frequency
|
||||
* @return int The direction in which the matching capacitor should be turned. 1 for clockwise, -1 for anticlockwise
|
||||
*
|
||||
* @example getMatchRotation(100000000); // returns the direction in which the matching capacitor should be turned at 100MHz to decrease the reflection
|
||||
*/
|
||||
int getMatchRotation(uint32_t current_resonance_frequency);
|
||||
|
||||
/**
|
||||
* @brief This function controls the stepper so that they hit the limit and stall. It then returns the current position of the stepper.
|
||||
*
|
||||
* @param stepper The stepper that should be stalled
|
||||
* @return int The current position of the stepper
|
||||
*
|
||||
* @example stallStepper(tuner); // stalls the tuner stepper and returns the current position
|
||||
*/
|
||||
int stallStepper(Stepper stepper);
|
||||
|
||||
/**
|
||||
* @brief This function performs a homing of the steppers. It returns the current position of the stepper.
|
||||
*
|
||||
* @param stepper The stepper that should be homed
|
||||
* @return long The current position of the stepper
|
||||
*
|
||||
* @example homeStepper(tuner); // homes the tuner stepper and returns the current position
|
||||
*/
|
||||
long homeStepper(Stepper stepper);
|
||||
|
||||
/**
|
||||
* @brief This function checks if the input is valid. It checks if the frequency is within the allowed range.
|
||||
*
|
||||
* @param frequency_MHz The frequency that should be checked in MHz.
|
||||
* @return uint32_t The frequency in Hz if the input is valid, 0 otherwise.
|
||||
*
|
||||
* @example validateInput(100); // returns 100000000U
|
||||
*/
|
||||
uint32_t validateInput(float frequency_MHz);
|
||||
/**
|
||||
* * @brief This method should be called when one wants to print information to the serial port.
|
||||
*
|
||||
* @param text The text that should be printed to the serial monitor. It should be a string
|
||||
* @return void
|
||||
*
|
||||
* @example printInfo("This is a test"); // prints "iThis is a test"
|
||||
*/
|
||||
void printInfo(String text);
|
||||
|
||||
/**
|
||||
* @brief This method should be called when one wants to print information to the serial port.
|
||||
*
|
||||
* @param number The number that should be printed to the serial monitor. It should be a number
|
||||
* @return void
|
||||
*
|
||||
* @example printInfo(123U); // prints "i123"
|
||||
*/
|
||||
void printInfo(uint32_t number);
|
||||
|
||||
/**
|
||||
* @brief This method should be called when one wants to an error to the serial port.
|
||||
*
|
||||
* @param text The text that should be printed to the serial monitor. It should be a string
|
||||
* @return void
|
||||
*
|
||||
* @example printError("Duck not found"); // prints "eDuck not found"
|
||||
*/
|
||||
void printError(String text);
|
30
src/commands/Command.h
Normal file
30
src/commands/Command.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Command.h
|
||||
#ifndef COMMAND_H
|
||||
#define COMMAND_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class Command {
|
||||
public:
|
||||
/**
|
||||
* @brief Executes the command.
|
||||
* Pure virtual function that must be implemented by derived classes.
|
||||
*/
|
||||
virtual void execute(String input_line) = 0;
|
||||
|
||||
/**
|
||||
* @brief Prints the result of the command.
|
||||
* Pure virtual function that must be implemented by derived classes.
|
||||
*/
|
||||
virtual void printResult() = 0;
|
||||
|
||||
/**
|
||||
* @brief Prints the help message of the command.
|
||||
* Pure virtual function that must be implemented by derived classes.
|
||||
*/
|
||||
virtual void printHelp() = 0;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
33
src/commands/FrequencySweep.cpp
Normal file
33
src/commands/FrequencySweep.cpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include "Utilities.h"
|
||||
#include "FrequencySweep.h"
|
||||
|
||||
void FrequencySweep::execute(String input_line) {
|
||||
printInfo("Started frequency sweep");
|
||||
// Get the start frequency which is the value until the next f character
|
||||
char delimiter = 'f';
|
||||
// Indices for each variable
|
||||
int startFreqIndex = input_line.indexOf(delimiter) + 1;
|
||||
int stopFreqIndex = input_line.indexOf(delimiter, startFreqIndex) + 1;
|
||||
int freqStepIndex = input_line.indexOf(delimiter, stopFreqIndex) + 1;
|
||||
|
||||
// Extract each variable from the string
|
||||
uint32_t startFreq = input_line.substring(startFreqIndex, stopFreqIndex - 1).toInt(); // Subtract 1 to not include the delimiter
|
||||
uint32_t stopFreq = input_line.substring(stopFreqIndex, freqStepIndex - 1).toInt();
|
||||
uint32_t freqStep = input_line.substring(freqStepIndex).toInt(); // If no second parameter is provided, substring() goes to the end of the string
|
||||
|
||||
// The find current resonance frequency also prints prints the S11 data to the serial monitor
|
||||
resonance_frequency = findCurrentResonanceFrequency(startFreq, stopFreq, freqStep);
|
||||
}
|
||||
|
||||
void FrequencySweep::printResult() {
|
||||
// This tells the PC that the frequency sweep is finished
|
||||
Serial.print("r");
|
||||
printInfo(resonance_frequency);
|
||||
}
|
||||
|
||||
void FrequencySweep::printHelp() {
|
||||
Serial.println("Frequency sweep command");
|
||||
Serial.println("Syntax: f<start frequency>f<stop frequency>f<frequency step>");
|
||||
Serial.println("Example: f100000000f200000000f50000");
|
||||
Serial.println("This will sweep the frequency from 100 MHz to 200 MHz with a step of 50 kHz");
|
||||
}
|
18
src/commands/FrequencySweep.h
Normal file
18
src/commands/FrequencySweep.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef FREQUENCYSWEEP_H
|
||||
#define FREQUENCYSWEEP_H
|
||||
|
||||
#include "Command.h"
|
||||
|
||||
/**
|
||||
* @brief This class is used to perform a frequency sweep
|
||||
*/
|
||||
class FrequencySweep : public Command {
|
||||
public:
|
||||
void execute(String input_lne) override;
|
||||
void printResult() override;
|
||||
void printHelp() override;
|
||||
private:
|
||||
uint32_t resonance_frequency;
|
||||
};
|
||||
|
||||
#endif
|
27
src/commands/Homing.cpp
Normal file
27
src/commands/Homing.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include "Utilities.h"
|
||||
#include "Homing.h"
|
||||
|
||||
void Homing::execute(String input_line) {
|
||||
printInfo("Homing...");
|
||||
tuner.STEPPER.setCurrentPosition(homeStepper(tuner));
|
||||
matcher.STEPPER.setCurrentPosition(homeStepper(matcher));
|
||||
}
|
||||
|
||||
void Homing::printResult() {
|
||||
printInfo("Resonance frequency after homing:");
|
||||
uint32_t startf = 35000000U;
|
||||
uint32_t stopf = 110000000U;
|
||||
uint32_t stepf = 100000U;
|
||||
uint32_t resonance_frequency = findCurrentResonanceFrequency(startf, stopf, stepf / 2);
|
||||
printInfo(resonance_frequency);
|
||||
Serial.print("r");
|
||||
printInfo("Homing finished");
|
||||
}
|
||||
|
||||
void Homing::printHelp() {
|
||||
Serial.println("Homing command");
|
||||
Serial.println("Syntax: h");
|
||||
Serial.println("Example: h");
|
||||
Serial.println("This will home the tuner and matcher");
|
||||
}
|
||||
|
15
src/commands/Homing.h
Normal file
15
src/commands/Homing.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#ifndef HOMING_H
|
||||
#define HOMING_H
|
||||
|
||||
#include "Command.h"
|
||||
|
||||
class Homing : public Command {
|
||||
public:
|
||||
void execute(String input_line) override;
|
||||
void printResult() override;
|
||||
void printHelp() override;
|
||||
private:
|
||||
uint32_t resonance_frequency;
|
||||
};
|
||||
|
||||
#endif
|
50
src/commands/TuneMatch.cpp
Normal file
50
src/commands/TuneMatch.cpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
#include "Utilities.h"
|
||||
#include "TuneMatch.h"
|
||||
|
||||
void TuneMatch::execute(String input_line){
|
||||
float target_frequency_MHz = input_line.substring(1).toFloat();
|
||||
uint32_t target_frequency = validateInput(target_frequency_MHz);
|
||||
if (target_frequency == 0)
|
||||
return;
|
||||
|
||||
uint32_t startf = 35000000U;
|
||||
uint32_t stopf = 110000000U;
|
||||
uint32_t stepf = 100000U;
|
||||
printInfo("Tuning and Matching to target frequency in MHz (automatic mode):");
|
||||
printInfo(target_frequency_MHz);
|
||||
|
||||
uint32_t resonance_frequency = automaticTM(target_frequency, startf, stopf, stepf);
|
||||
}
|
||||
|
||||
|
||||
uint32_t TuneMatch::automaticTM(uint32_t target_frequency, uint32_t start_frequency, uint32_t stop_frequency, uint32_t frequency_step)
|
||||
{
|
||||
uint32_t resonance_frequency = findCurrentResonanceFrequency(start_frequency, stop_frequency, frequency_step);
|
||||
|
||||
DEBUG_PRINT("Resonance Frequency before TM");
|
||||
DEBUG_PRINT(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;
|
||||
}
|
||||
|
||||
void TuneMatch::printResult()
|
||||
{
|
||||
Serial.print("r");
|
||||
printInfo(resonance_frequency);
|
||||
}
|
||||
|
||||
void TuneMatch::printHelp()
|
||||
{
|
||||
Serial.println("Tune and Match command");
|
||||
Serial.println("Syntax: d<target frequency in MHz>");
|
||||
Serial.println("Example: d100");
|
||||
Serial.println("This will tune and match to 100 MHz");
|
||||
}
|
17
src/commands/TuneMatch.h
Normal file
17
src/commands/TuneMatch.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#ifndef TUNEMATCH_H
|
||||
#define TUNEMATCH_H
|
||||
|
||||
#include "Command.h"
|
||||
|
||||
class TuneMatch : public Command {
|
||||
public:
|
||||
void execute(String input_line) override;
|
||||
void printResult() override;
|
||||
void printHelp() override;
|
||||
private:
|
||||
uint32_t automaticTM(uint32_t target_frequency, uint32_t start_frequency, uint32_t stop_frequency, uint32_t frequency_step);
|
||||
uint32_t resonance_frequency;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
21
src/global.h
Normal file
21
src/global.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#ifndef GLOBAL_H
|
||||
#define GLOBAL_H
|
||||
|
||||
#include <TMC2130Stepper.h>
|
||||
#include <AccelStepper.h>
|
||||
#include <MultiStepper.h>
|
||||
#include <math.h>
|
||||
#include "ADF4351.h"
|
||||
#include "AD5593R.h"
|
||||
|
||||
#include "Pins.h" // Pins are defined here
|
||||
#include "Stepper.h"
|
||||
#include "Positions.h" // Calibrated frequency positions are defined her
|
||||
|
||||
// We want these objects to be accessible from all files
|
||||
extern ADF4351 adf4351;
|
||||
extern Stepper tuner;
|
||||
extern Stepper matcher;
|
||||
extern AD5593R adac;
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue