[{"content":"App notifications should have a spam filter, and users should be able to help train it.\nWe have already accepted that email needs spam filtering. Nobody expects people to manually inspect every dubious message and individually configure every sender. Yet this is effectively what phones ask us to do with app notifications.\nApps send delivery updates, payment alerts, chat messages, discounts, streak reminders, invented deadlines and “we miss you” messages through the same channel. The phone then asks us to open settings, find the app, understand its categories and turn them off one by one.\nThat is not meaningful control. It is outsourced admin work.\nThe problem is not the app. It is the notification. A shopping app may need to tell me that my order has arrived. It does not need to interrupt me because a sale ends tonight.\nA bank may need to alert me about a suspicious transaction. It does not need to use the same attention channel to market a personal loan.\nCurrent notification settings are built around apps and categories. Android does provide notification permissions and channels, which is better than having no control at all. [1][2] But the useful unit is smaller: the individual notification.\nPeople should be able to tap a notification and mark it as spam.\nNot “I never want to hear from this app.” Just: this message was unwanted, repetitive or promotional.\nBuild an independent notification firewall I would not leave this entirely to Google, Apple or device manufacturers.\nThey control the platform, but their incentives are not identical to the user’s. Their ecosystems benefit when apps are engaged with, opened and retained. Digital Wellbeing tools are useful for setting limits. They do not solve the underlying question of which alerts deserve to interrupt someone in the first place.\nThe answer should be an independent notification firewall. It would sit between app notifications and the user, with explicit permission and a privacy-first design.\nThe first part is an on-device classifier. It would look at signals such as notification frequency, duplicate wording, time of day, whether the user started an action that makes an update expected, and whether similar alerts are normally opened or dismissed.\nThe second part is a shared spam registry. When users mark substantially similar notifications as spam, the central service receives an anonymised aggregate signal. If enough people report the same kind of alert from the same app or category, it receives a spam reputation.\nThis must not become a cloud copy of everyone’s private messages. The default should be local processing. The registry should receive only the minimum information required to identify a pattern: app identity, category, privacy-preserving notification signature and aggregate feedback.\nCollective feedback, personal control One person marking a notification as spam should improve their own phone immediately.\nMany people doing the same should improve the system for everyone.\nBut a shared spam score should inform the user, not overrule them. Someone may genuinely want every offer from a retailer. Someone else may want none. The firewall should make that choice simple:\nAllow now: show the notification normally. Group or digest: place low-priority alerts together at the bottom of the notification screen, or show them at a time chosen by the user. Hide: keep suspected-spam alerts out of the main notification screen, while retaining a separate spam folder for review. The user should always be able to promote an app, category or individual notification again. An algorithm will occasionally get it wrong. The point is to give the user a good default, not create another opaque gatekeeper.\nMeasure interruptions, not only screen time Screen-time reports show how long someone used a phone. They do not show how often the phone pulled them away from something else.\nThat is the more useful measure.\nA weekly interruption report could show the number of notifications received, the number that actively interrupted the user, the number grouped into a digest and the number hidden as suspected spam. It could also show which apps have the worst interruption-to-usefulness ratio.\nResearch suggests smartphone notifications can disrupt attention and cognitive control. [3] That does not mean every alert is harmful. It means interruption has a cost, and the system should recognise that cost.\nTurn “Attention Is All You Need” upside down The 2017 paper Attention Is All You Need helped transform machine learning by showing how systems can focus on relevant information. [4]\nThe app economy seems to have adopted a different version of the idea: attention is all it needs from us.\nEvery “last chance” sale, streak warning and generic reminder is built around the assumption that it deserves immediate access to a person. The user is then given a settings page and told they are in control.\nWe need to reverse that assumption.\nAttention is not an app’s default entitlement. It is a limited personal resource. An app should earn the right to interrupt someone, and users should be able to collectively identify the notifications that abuse that right.\nAndroid can support an early version because its notification-listener capability can receive system callbacks when notifications are posted, removed or re-ranked. [5] iOS is more restricted, so a true independent cross-platform solution would require Apple to expose a secure, user-authorised notification-filtering interface.\nThat is not a reason to drop the idea. It is the policy demand.\nSources [1] Android Developers, “Notification runtime permission”\nhttps://developer.android.com/develop/ui/compose/notifications/notification-permission\n[2] Android Developers, “Create a notification”\nhttps://developer.android.com/develop/ui/compose/notifications/create-notification\n[3] Upshaw et al., “The effects of smartphone notifications on cognitive control and attention”\nhttps://pmc.ncbi.nlm.nih.gov/articles/PMC9671478/\n[4] Vaswani et al., “Attention Is All You Need”\nhttps://arxiv.org/abs/1706.03762\n[5] Android Developers, “NotificationListenerService”\nhttps://developer.android.com/reference/android/service/notification/NotificationListenerService\n","permalink":"https://manujg.com/notes/notification-spam-firewall/","summary":"A proposal for an independent, user-led spam filter for app notifications.","title":"Attention Is All They Need. Yours."},{"content":"These days, everyone seems upset with young employees.\n\u0026ldquo;They don\u0026rsquo;t work hard.\u0026rdquo;\n\u0026ldquo;They want work-life balance.\u0026rdquo;\n\u0026ldquo;They ask too many questions.\u0026rdquo;\n\u0026ldquo;They leave companies too quickly.\u0026rdquo;\nYes, they ask questions.\n\u0026ldquo;What exactly do you want me to do?\u0026rdquo;\n\u0026ldquo;Why are we doing it?\u0026rdquo;\n\u0026ldquo;What is more important?\u0026rdquo;\n\u0026ldquo;Will this be my job, or will I also get five other jobs because I finished this one?\u0026rdquo;\nVery unreasonable behaviour.\nIn many companies, a manager gives a vague task and says, \u0026ldquo;Take ownership.\u0026rdquo; When the person asks what the result should be, they are told they lack initiative.\nIf the manager does not know what result is needed, how is the employee supposed to take ownership of it? Telepathy is still not part of the joining kit.\nThis starts much earlier. In school, we teach children to give the right answer. Don\u0026rsquo;t make mistakes. Don\u0026rsquo;t question too much. Get marks.\nThen they join a company. Follow the process. Meet the target. Don\u0026rsquo;t upset anyone. Get a good rating.\nAfter years of teaching them to follow instructions, we suddenly ask: \u0026ldquo;Why don\u0026rsquo;t you think independently?\u0026rdquo;\nWe want fresh thinking. As long as it agrees with ours.\nI have seen young engineers come with good ideas. They want to improve something. They want to solve a real problem.\nThen their idea gets approved.\nWhich means they now have to implement it, explain it in three meetings, keep doing their old work, and answer why it is not finished yet.\nGood idea. Bad luck.\nIf the idea fails, the engineer gets blamed. The manager who approved it also gets blamed. Soon everybody learns the safest option: do your assigned work, keep quiet, and do not create trouble.\nAfter that, we call them unambitious.\nThe same people are also called disloyal when they leave for better pay, better work or a better manager. Companies hire when they need people and fire when they do not. That is called business.\nBut when an employee does the same thing, apparently the values have fallen.\nI do not think this generation is weak. I think they are more practical. They want clear work, fair pay, some freedom and a life outside office. This is not a shocking demand.\nI have seen young people do very good work when they are given a real problem, room to think and support when things go wrong.\nMaybe instead of blaming them, we should make work worth caring about.\n","permalink":"https://manujg.com/notes/maybe-young-generation-is-not-the-problem/","summary":"Young employees are often blamed for asking clear questions, wanting a life outside work, and changing jobs. I see those as sensible responses to unclear work and one-sided expectations.","title":"Maybe the Young Generation Is Not the Problem"},{"content":"Context Short network failures were breaking calls and SSH sessions but did not last long enough to appear in ordinary speed tests.\nApproach A custom monitor measured three network hops separately: the laptop to mesh node, mesh node to router, and router to the ISP.\nOutcome The experiment made it possible to identify where micro-outages occurred instead of treating the whole connection as one opaque service.\nWhat I learned Small, focused observability tools can be more useful than broad dashboards when the failure mode is specific.\nRead the Note\n","permalink":"https://manujg.com/case-studies/network-resilience-monitor/","summary":"A practical network-monitoring experiment focused on locating failures that ordinary speed tests missed.","title":"Case Study: Finding Micro-Outages in a Multi-Hop Network"},{"content":"Context Before committing to a hardware design, I wanted a faster way to explore ADC metrology behaviour, noise and harmonic distortion.\nApproach I used AI-assisted development to build an indicative STM32H7 ADC digital twin and then exposed the result through a browser-based simulator. The model is explicitly a proof of concept, not a substitute for datasheet validation or production verification.\nOutcome The experiment made the trade-offs visible early and created a working tool that could be used to discuss accuracy, distortion and test scenarios before hardware work.\nWhat I learned AI can accelerate the first working model, but engineering judgement, source verification and clear limits on what the model proves remain essential.\nRead the technical Note · Open the Lab\n","permalink":"https://manujg.com/case-studies/rapid-embedded-prototyping/","summary":"How a digital-twin experiment was used to explore metrology constraints before committing to a hardware design.","title":"Case Study: Rapid Embedded Prototyping with AI"},{"content":"I have been using OpenClaw for several months as a local automation layer for reminders, scheduled jobs, notifications and household workflows.\nThe setup uses a layered notification architecture. The core jobs and business logic remain channel-neutral. A separate notification router decides whether an event should be delivered through Telegram, WhatsApp or both. This keeps the automation layer independent of any one messaging platform.\nThat separation became especially useful while troubleshooting WhatsApp. After upgrading to OpenClaw 2, I experienced gateway restarts, crashes, unreliable reminders, WhatsApp Web session conflicts, QR relinking issues and problems with self-chat message events. Telegram became a reliable fallback while WhatsApp was being repaired.\nThe main lesson was simple: notification delivery should remain separate from the middle layer that performs the actual work.\nOpenClaw 2 can understand natural-language instructions and turn them into practical actions. I use it to manage scheduled reminders, monitor tasks and calendars, generate email-related alerts, prepare community-call notifications and coordinate recurring household workflows.\nDifferent notifications can also be routed through different channels depending on their urgency and purpose. For example, a lower-priority update may go through one channel, while an important reminder may be sent through both.\nOpenClaw 2 is powerful, but it still feels immature in some areas. Gateway restarts can be painful, the service occasionally crashes and requires manual intervention, and reminders can behave inconsistently. WhatsApp may also require relinking and careful session management.\nThese issues are manageable for a personal technical project, but they make the system less dependable than it should be.\nI use Claude, Gemini and ChatGPT extensively for programming and home automation. I recently stopped my Claude subscription and, at least for now, have not really missed it. ChatGPT has become my default engine for much of my work.\nIts Work feature has been a breakthrough because it removes friction from tasks that previously required repeated manual intervention. I also enjoy ChatGPT’s voice mode considerably more than the voice experiences I have tried with Gemini and other tools so far.\nThat may change as these products evolve. For now, ChatGPT remains my preferred tool for programming, automation, voice interaction and day-to-day work, while OpenClaw provides a useful local automation layer around them.\n","permalink":"https://manujg.com/notes/openclaw-chatgpt-claude-gemini/","summary":"How I use OpenClaw as a local automation layer alongside ChatGPT, Claude and Gemini, including a channel-neutral notification architecture and the practical limits I have encountered.","title":"Using OpenClaw Alongside ChatGPT, Claude and Gemini"},{"content":"I have been experimenting with turning an old Kindle Paperwhite into a simple personal dashboard.\nThe Kindle browser loads a lightweight web page hosted separately from the data source. Google Apps Script acts as the backend. It reads calendar events through Google Calendar and retrieves tasks from all my Google Tasks lists using Google\u0026rsquo;s Tasks service. The script then returns a small JSON response containing events, task categories and due dates.\nThe dashboard displays this information in two Kindle-friendly sections. Calendar events are arranged by time, while tasks retain the order of their Google Tasks lists. Long event and task names wrap within the available screen width.\nAuto-scrolling is optional and switched off by default. It can be enabled when there is more information than the screen can display, while manual interaction can pause it.\nThe project has involved practical challenges including browser compatibility, limited screen space, scrolling behaviour, data refreshes and keeping the interface useful on basic hardware.\nWith AI assistance, it has become much easier to experiment with ideas like this and build working prototypes quickly. Tasks that would earlier have required considerable research and development effort can now be explored, tested and refined in a much shorter time.\nThis remains a small experiment, but it demonstrates how inexpensive hardware and simple web services can be combined into a useful personal tool.\n","permalink":"https://manujg.com/notes/kindle-paperwhite-personal-dashboard/","summary":"A small experiment using a Kindle, Google Apps Script and AI to build a low-cost personal dashboard.","title":"Turning an Old Kindle into a Personal Dashboard"},{"content":"I have been reading about a few developments that may affect Indian IT. They are not the complete picture, but they are useful inputs into a small scenario-mapping experiment I am building.\n1. H-1B costs, dependents and delivery choices The US has proposed a substantial additional fee for cap-subject H-1B petitions.1 This could make onsite deployment more expensive. There is also a family aspect. If dependent spouses cannot work under any future policy change, an onsite role may become less attractive for married people and existing expat families. That can create disruption even before a company changes its delivery model.\nThe outcome for Indian IT is not automatically positive or negative. More work may be delivered from India, but US local hiring may rise too. Clients are already building GCC capability in India, and some may bring more work into those centres rather than use the traditional services model.\n2. AI and employment uncertainty NDTV reported a Great Place To Work India survey in which nearly one in four CHROs expected AI-related workforce reductions of between 1% and 20% over two years.2 That is a survey result, not my forecast for Indian IT.\nThere is likely to be short-term uncertainty and some hiring reduction while companies allocate budgets to AI experimentation and automate parts of routine work. Over time, other roles should emerge. Areas such as mechanical engineering, robotics, drones, embedded systems and industrial AI may create different kinds of demand. The important question is whether Indian companies and professionals capture that work.\n3. Japanese GCCs in India Japanese GCC activity in India is a positive input. Deloitte says more than 100 Japanese firms operate GCCs here, with work in product R\u0026amp;D, AI, engineering and digital manufacturing.3 If this starts to scale further, it should be net positive for India, particularly for engineering-led work rather than only traditional outsourcing.\nI am doing a short experiment by building an Indian IT Scenario Simulator. As new evidence appears, I will update the model and keep its changelog visible.\nFor this update, H-1B is mixed / uncertain; AI is mixed, with short-term pressure and longer-term opportunity; and Japanese GCCs are positive, but confidence-weighted because the direction is constructive while the eventual scale is not yet known.\nUSCIS: DHS proposes additional H-1B fee\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nNDTV: Great Place To Work India survey\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nDeloitte: Japanese GCCs in India\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","permalink":"https://manujg.com/notes/indian-it-h1b-ai-japanese-gccs/","summary":"Three current inputs to an Indian IT scenario-mapping experiment: H-1B and family mobility, AI-led employment uncertainty, and Japanese GCC growth.","title":"The Current Landscape of Indian IT: H-1B, AI Impacts and the Rise of Japanese GCCs in India"},{"content":"Can public information be combined into a simple calendar that helps people decide when to travel in Bengaluru?\nThis proof of concept uses historical weekday/time traffic patterns together with public holidays, school-holiday effects, long-weekend behaviour, weather and published area rush-hour information. It does not use Google traffic data and it is not intended to replace navigation apps.\nThe experiment separates two questions:\nhow busy the roads are likely to be at that time; and whether that particular date is likely to be lighter or heavier than a normal comparable day. It is deliberately labelled as a POC because the estimates still need real-world calibration and may be incomplete or wrong.\nOpen the Bengaluru Traffic-Risk Calendar →\nYou can also find it in Labs.\n","permalink":"https://manujg.com/notes/bengaluru-traffic-risk-calendar/","summary":"An experiment using public historical traffic patterns, holidays, long weekends, weather and area-specific peak timings to estimate when Bengaluru roads may be easier or harder than usual.","title":"Bengaluru Traffic-Risk Calendar POC"},{"content":"This is the workflow I follow these days.\nI have been building some large applications from scratch using AI, actual complex codebases, not just scripts or quick utilities. And honestly, the term vibe coding is a bit misleading. For something small, it is fine. For anything large, it falls apart pretty quickly.\nI use several tools right now: Antigravity, Claude Code, Codex, Cursor, Windsurf, and OpenCode. The specific tool matters less than the core problem, which is the same across all of them.\nThe AI forgets the architecture. It hallucinates dependencies. And at some point, it will suggest the same broken fix it tried fifteen prompts ago, completely unaware it already tried this. You end up burning tokens, going in circles.\nYou have to actively manage it. Here is what I do.\n1. requirements.md — the anchor Chat history is not a reliable context. It degrades, and the AI starts drifting.\nI keep a requirements.md in the root of every project covering the system objectives, core requirements, and architectural constraints. When the AI goes off in some direction I didn\u0026rsquo;t ask for, I just say: \u0026ldquo;Stop. Read requirements.md and realign.\u0026rdquo; No back and forth.\n2. tasks.md — the roadmap I use tasks.md for tracking what needs to be done, but its real value is making the AI pace itself.\nGive it something complex and it will immediately try to write 800 lines across four files, hit its token limit halfway, and leave you with broken syntax. So I make it stop, break the work into small chunks, document those chunks in tasks.md first, and then implement only the first one. It seems like extra steps, but it saves a lot of time overall.\n3. memory.md — the fix for AI amnesia This is the one I find most useful.\nAI has no memory of what didn\u0026rsquo;t work. You hit a bug, it tries Solution A, that fails, it tries B, B fails, it comes back to A again as if nothing happened. So I maintain a memory.md that logs what has already been built, what approaches failed, and why. Every time something doesn\u0026rsquo;t work, I prompt: \u0026ldquo;That failed. Update memory.md with what we tried and why it broke.\u0026rdquo; Before any complex fix: \u0026ldquo;Read memory.md before suggesting anything.\u0026rdquo;\nWorth keeping this file trimmed. It can get large and start eating into your context window. Log the reasoning, not the full code attempts.\n4. Model routing Frontier models cost money. Running everything through Claude Opus or Gemini 3.1 Pro for every small change adds up.\nI use the heavy models for the decisions — architecture, requirements, and breaking down complex problems. Once that\u0026rsquo;s done, I switch to Gemini Flash for the smaller implementation chunks. You save a lot on tokens without losing quality where it actually matters.\n5. Rules file — .cursorrules or .windsurfrules The requirements file says what to build. The rules file says how — tech stack, forbidden libraries, and architectural constraints. Things like \u0026ldquo;don\u0026rsquo;t touch the abstraction layer directly.\u0026rdquo; Without this, the AI will happily pull in some random library you\u0026rsquo;ve never heard of to solve something that didn\u0026rsquo;t need it.\nI don\u0026rsquo;t personally do TDD, so this file and the CI/CD pipeline are what keep the structure from drifting. If you do TDD, getting the AI to write failing tests before writing code is a good way to keep its output bounded.\n6. CI/CD — because AI breaks things without noticing A small change to a helper function can break something completely unrelated three folders away. The AI won\u0026rsquo;t catch it.\nEvery commit runs the full test suite via GitHub Actions. If the build breaks, I revert, paste the error log back into the prompt, and start again. No exceptions on this — once you start letting broken commits pile up, the whole thing becomes very hard to untangle.\n7. The wiki as entry point — something I am still figuring out Karpathy posted something in early April 2026 — a system where an LLM compiles and maintains an interlinked wiki from raw source material. People implementing it reported session startup tokens dropping by around 90%, because instead of loading many raw files every session, the AI reads a compact compiled index instead.\nI was already keeping a markdown file per project covering architecture, file structure, and key decisions. But I was using it as a reference, not as an entry point. That difference is real — reference means you read it occasionally, entry point means every session starts there before the AI does anything.\nI am experimenting with this now. Whether the token savings carry over to a coding workflow the same way, I honestly don\u0026rsquo;t know yet.\nEven with all of this, the AI still goes wrong. Regularly.\nA good chunk of my day is still typing things like: \u0026ldquo;You are drifting. Read requirements.md, check tasks.md, log what just failed in memory.md.\u0026rdquo;\nThis workflow does not make AI autonomous. It just stops it from wasting your time in the ways it most commonly does. The actual work shifts from writing code to keeping the AI pointed at the right problem, which is a different skill and takes a while to get used to.\n","permalink":"https://manujg.com/notes/agentic-workflow-vibe-coding/","summary":"Vibe coding works for scripts. It falls apart on large codebases. Here is the exact multi-file workflow I use to stop AI from hallucinating, looping, and burning tokens.","title":"The Agentic Workflow I Actually Use for Big Codebases"},{"content":"The Problem My internet kept dropping. Not for long — maybe 5 to 50 seconds at a time. But enough to break calls and SSH sessions. And because the drops were so short, they never showed up on a speed test.\nMy home network has three hops:\nHop 1 — Laptop → Mesh Node Hop 2 — Mesh Node → Main Router Hop 3 — Main Router → ISP I needed to know which hop was failing. What I Built A Python script that pings all three hops every couple of seconds and logs the results. When a drop happens, it checks which hops were still responding to work out where the failure was — Mesh, Router, or ISP. Everything goes into a local SQLite database. A small Flask dashboard shows the latency graph and the outage log.\nThat\u0026rsquo;s it. No more than what I needed.\nThe dashboard runs locally and shows:\nReal-time latency graph across all three hops Automated root cause label on each outage — Mesh, Router, or ISP 24-hour outage log with timestamps and duration Running totals: outage count and cumulative downtime Live dashboard — two ISP-sourced outages in the 24h window. Total packet loss: 0.02%.\nIn my case both logged outages pointed to the ISP. The Mesh and Router stayed up both times. Good to know before picking up the phone.\nHow I Built It I didn\u0026rsquo;t Google for an existing tool. I didn\u0026rsquo;t spend time on Stack Overflow. I described the setup to Gemini, the three hops, what should count as an outage and what I wanted to see on the dashboard. About 20 minutes later, I had working code.\nI specified what each hop represented, what should count as an outage, what the dashboard needed to show. The AI handled the implementation. I handled the thinking about the problem.\nI did the thinking about the problem. The barrier isn\u0026rsquo;t writing code anymore. The barrier is being clear about what you actually need.\nThe Bigger Point: Personal Software We are entering an era of personal software. Everyone will have more custom tools, built specifically for their setup, their workflow, their exact problem — not a generic tool that almost fits.\nThis is what changes as AI coding gets better. The cost of building something specific drops so much that there\u0026rsquo;s no reason to settle for a generic tool that almost fits. You just describe what you need and build it.\nIn the past month alone, I\u0026rsquo;ve written more code than in the previous few years combined. Most of it I haven\u0026rsquo;t published anywhere. It\u0026rsquo;s just running in the background — automating things, tracking things, solving specific problems I had. About 10% of what I build ends up on GitHub. The rest is personal infrastructure.\nThe implication isn\u0026rsquo;t that software engineers disappear. It\u0026rsquo;s that software explodes. Far more of it, far more specific to each person\u0026rsquo;s situation, and far less of it ever gets sold as a product — because it doesn\u0026rsquo;t need to. It just needs to work for you.\nSource code: github.com/manujguptain/NetworkOutageMonitor\nClone it, set your three IPs in the config block at the top, run python app.py and open localhost:5000.\n","permalink":"https://manujg.com/notes/triple-hop-network-audit/","summary":"My connection kept dropping — short blips, 5 to 50 seconds. Too short for a speed test to catch. So I built something that could.","title":"Triple-Hop Network Audit: Catching Micro-Outages with a Custom Monitor"},{"content":"I got tired of paying for something that a browser can do perfectly well. So I vibe-coded this up — free for everyone, forever.\nEverything runs locally in your browser. No photo ever leaves your device. No account. No subscription.\nWhat it does: Crop to any international standard — US, UK, India (Passport, Visa, OCI), Schengen/EU, Japan, China, Australia, Canada, and more. Free sizing — enter exact dimensions in mm if your country\u0026rsquo;s requirements aren\u0026rsquo;t listed. Pan \u0026amp; zoom — drag the photo inside the crop frame to get the framing just right. Brightness \u0026amp; contrast — fine-tune the image before export. Print sheet layout — arrange multiple copies on standard paper sizes (4×6″, 5×7″, A4, Letter) with adjustable margins and gaps. Portrait \u0026amp; landscape — choose your print orientation. Download options — single photo or full print-ready sheet. Choose JPEG (recommended for passport submissions) or your original format. 100% private — all processing happens in your browser. Nothing is uploaded anywhere. → Launch the Passport Photo Editor\nDisclaimer: This tool does not guarantee compliance with any specific regional authority\u0026rsquo;s photo requirements. Always verify the current specifications directly with the issuing authority before submitting.\nBuilt with plain HTML, CSS, and JavaScript. No frameworks, no dependencies, no nonsense.\n","permalink":"https://manujg.com/notes/passport-photo-editor/","summary":"\u003cp\u003eI got tired of paying for something that a browser can do perfectly well. So I vibe-coded this up — \u003cstrong\u003efree for everyone, forever\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eEverything runs locally in your browser. No photo ever leaves your device. No account. No subscription.\u003c/p\u003e\n\u003ch3 id=\"what-it-does\"\u003eWhat it does:\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eCrop to any international standard\u003c/strong\u003e — US, UK, India (Passport, Visa, OCI), Schengen/EU, Japan, China, Australia, Canada, and more.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eFree sizing\u003c/strong\u003e — enter exact dimensions in mm if your country\u0026rsquo;s requirements aren\u0026rsquo;t listed.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003ePan \u0026amp; zoom\u003c/strong\u003e — drag the photo inside the crop frame to get the framing just right.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eBrightness \u0026amp; contrast\u003c/strong\u003e — fine-tune the image before export.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003ePrint sheet layout\u003c/strong\u003e — arrange multiple copies on standard paper sizes (4×6″, 5×7″, A4, Letter) with adjustable margins and gaps.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003ePortrait \u0026amp; landscape\u003c/strong\u003e — choose your print orientation.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eDownload options\u003c/strong\u003e — single photo or full print-ready sheet. Choose JPEG (recommended for passport submissions) or your original format.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e100% private\u003c/strong\u003e — all processing happens in your browser. Nothing is uploaded anywhere.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003e\u003ca href=\"/labs/passport-photo/\"\u003e→ Launch the Passport Photo Editor\u003c/a\u003e\u003c/strong\u003e\u003c/p\u003e","title":"Passport Photo Editor"},{"content":"I own an Ather 450X and I ride it in Bengaluru. The range estimate is genuinely accurate — better than I expected honestly. But there\u0026rsquo;s something about the savings tracker that\u0026rsquo;s been bothering me for a while and I want to write it down.\nThe short version: the savings tracker only counts kilometres. It doesn\u0026rsquo;t count the electricity you spend keeping the scooter alive while it\u0026rsquo;s parked. For anyone who rides daily, this probably doesn\u0026rsquo;t matter much. For anyone who uses the scooter a few times a week or travels for work — it\u0026rsquo;s the whole story.\nWhat the tracker actually measures The formula, as best as I can tell from how Ather reports their fleet numbers, is this:\nKilometres travelled × (assumed petrol cost per km − assumed electricity cost per km)\nAther\u0026rsquo;s 2024 year-end data showed ₹6.2 billion in savings across 2.39 billion kilometres [¹]. That maths works only if savings track linearly with distance. Which means if you rode zero kilometres this month, the app shows zero electricity spent.\nThe scooter doesn\u0026rsquo;t agree.\nThe idle drain problem Ather scooters run background systems constantly — BMS, 4G telemetry, various connected features. Even when parked. The Ather community has been pretty consistent on this: somewhere between 3–5% of battery capacity lost per day while idle [²].\nOn the 3.7 kWh battery, 3% a day is roughly 111 Wh. A week parked is close to 1 kWh gone, without riding anywhere. You paid for that on your electricity bill. The savings tracker doesn\u0026rsquo;t see it.\nThe practical problem I\u0026rsquo;ve actually experienced: come back after ten days of travel, scooter needs charging before you can ride. The whole \u0026ldquo;just get on and go\u0026rdquo; convenience disappears exactly when you need it. You\u0026rsquo;re planning a charge before you can plan a ride. That\u0026rsquo;s an annoying inversion for something marketed on convenience.\nWhy petrol doesn\u0026rsquo;t have this problem Petrol mileage is a cleaner metric than people give it credit for. Litres purchased divided by kilometres ridden. The fuel doesn\u0026rsquo;t drain while the scooter sits in the parking lot. Come back after two weeks, it starts, you ride.\nThere are idle costs with petrol too — the 12V battery, oil aging with time. But they\u0026rsquo;re slow. The EV idle drain is measurable every single day the scooter isn\u0026rsquo;t moving.\nWhat would actually fix this The savings tracker should compare total kWh drawn from the wall — every charging session, including the ones that just topped up idle drain — against what the equivalent kilometres would have cost in petrol.\nFor a daily commuter this barely changes the numbers. For an occasional rider it\u0026rsquo;s a completely different picture.\nThe hardware to do this already exists in the scooter. The BMS tracks energy flow. This is a product decision, not an engineering constraint. A smart energy monitoring plug (₹1,000–₹1,500) will give you the real number in the meantime — it logs actual kWh per session rather than inferring it from distance.\nSources [¹] Ather Energy 2024 fleet data — Autocar Professional, January 2025.\nhttps://www.autocarpro.in/news/ather-reports-239-billion-kilometres-covered-by-its-ev-users-in-2024-124671\n[²] \u0026ldquo;Idle power consumption\u0026rdquo; thread, Ather Community Forum, July 2023.\nhttps://forum.atherenergy.com/t/idle-power-consumtion/173816\nDisclaimer: I own an Ather 450X. The numbers here are indicative — idle drain varies by model, software version, temperature and usage. Personal observation, not an audit.\n","permalink":"https://manujg.com/notes/ather-savings-calculator/","summary":"Ather\u0026rsquo;s savings tracker tells you how much you saved on petrol. It doesn\u0026rsquo;t count what you spent keeping the scooter alive while parked. For occasional riders, that gap is the whole story.","title":"The Mileage Mismatch: A Thought on Ather's Savings Tracker"},{"content":"This tool models the behavior of a 16-bit SAR ADC integrated with a DMA controller. It is designed to help engineers validate metrology algorithms (like RMS or FFT) against quantization noise and harmonic interference.\nKey Features: Harmonic Distortor: Inject 3rd and 5th harmonics to test THD resilience. Accuracy Class Check: Real-time compliance monitoring for Class 0.2, 0.5, and 1.0. Data Export: Generate synthetic 16-bit datasets for offline C/Python analysis. STM32H7 Metrology Lab: FFT \u0026 Delta Error Target Accuracy Class Class 0.2 (Utility) Class 0.5 (Industrial) Class 1.0 (Residential) Sample Rate: 4000 Hz ⚠️ METROLOGICAL GUARDRAIL: Below 10x oversampling. THD: 5% Export CSV DMA: READY WAVEFORM (1024-SAMPLE DMA) SPECTRUM (256-BIN WINDOW) TRUE RMS ERROR 0.00% DELTA ERROR (V) 0.000V INIT Looking for the technical deep-dive? Read the full analysis in the ADC Digital Twin Note.\n","permalink":"https://manujg.com/labs/stm32h7-adc-simulator/","summary":"\u003cp\u003eThis tool models the behavior of a 16-bit SAR ADC integrated with a DMA controller. It is designed to help engineers validate metrology algorithms (like RMS or FFT) against quantization noise and harmonic interference.\u003c/p\u003e\n\u003ch3 id=\"key-features\"\u003eKey Features:\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eHarmonic Distortor:\u003c/strong\u003e Inject 3rd and 5th harmonics to test THD resilience.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAccuracy Class Check:\u003c/strong\u003e Real-time compliance monitoring for Class 0.2, 0.5, and 1.0.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eData Export:\u003c/strong\u003e Generate synthetic 16-bit datasets for offline C/Python analysis.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv id=\"adc-simulator-container\" style=\"font-family: 'Segoe UI', sans-serif; padding: 25px; background: #fff; border: 1px solid #ddd; border-radius: 12px; color: #333; max-width: 850px; margin: auto; box-shadow: 0 4px 15px rgba(0,0,0,0.1);\"\u003e\n    \u003ch3 style=\"margin-top:0; color:#1a1a1a; border-bottom: 2px solid #007bff; padding-bottom: 10px;\"\u003eSTM32H7 Metrology Lab: FFT \u0026 Delta Error\u003c/h3\u003e\n    \n    \u003cdiv style=\"display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px;\"\u003e\n        \u003cdiv style=\"display: flex; flex-direction: column; gap: 12px;\"\u003e\n            \u003cdiv\u003e\n                \u003clabel style=\"font-size: 0.8em; font-weight: bold; color: #666;\"\u003eTarget Accuracy Class\u003c/label\u003e\n                \u003cselect id=\"target-class\" style=\"width:100%; padding:8px; border-radius:4px; border:1px solid #ccc;\"\u003e\n                    \u003coption value=\"0.002\"\u003eClass 0.2 (Utility)\u003c/option\u003e\n                    \u003coption value=\"0.005\" selected\u003eClass 0.5 (Industrial)\u003c/option\u003e\n                    \u003coption value=\"0.01\"\u003eClass 1.0 (Residential)\u003c/option\u003e\n                \u003c/select\u003e\n            \u003c/div\u003e\n            \u003cdiv\u003e\n                \u003clabel style=\"font-size: 0.8em; font-weight: bold; color: #666;\"\u003eSample Rate: \u003cspan id=\"fs-val\" style=\"color:#007bff\"\u003e4000\u003c/span\u003e Hz\u003c/label\u003e\n                \u003cinput type=\"range\" id=\"fs-slider\" min=\"100\" max=\"25000\" value=\"4000\" style=\"width:100%\"\u003e\n                \u003cdiv id=\"aliasing-warning\" style=\"font-size: 0.7em; color: #d9534f; margin-top: 4px; display: none; font-weight: bold;\"\u003e⚠️ METROLOGICAL GUARDRAIL: Below 10x oversampling.\u003c/div\u003e\n            \u003c/div\u003e\n        \u003c/div\u003e\n\n        \u003cdiv style=\"display: flex; flex-direction: column; gap: 12px; padding-left: 15px; border-left: 1px solid #eee;\"\u003e\n            \u003cdiv\u003e\n                \u003clabel style=\"font-size: 0.8em; font-weight: bold; color: #666;\"\u003eTHD: \u003cspan id=\"thd-val\" style=\"color:#e67e22\"\u003e5\u003c/span\u003e%\u003c/label\u003e\n                \u003cinput type=\"range\" id=\"thd-slider\" min=\"0\" max=\"40\" value=\"5\" style=\"width:100%\"\u003e\n            \u003c/div\u003e\n            \u003cdiv style=\"display: flex; gap: 10px;\"\u003e\n                \u003cbutton id=\"download-btn\" style=\"flex: 1; background: #28a745; color: white; border: none; padding: 10px; border-radius: 4px; cursor: pointer; font-weight: bold;\"\u003eExport CSV\u003c/button\u003e\n                \u003cdiv id=\"dma-status\" style=\"flex: 1; padding: 10px; border-radius: 4px; font-size: 0.7em; font-weight: bold; text-align: center; background: #eee;\"\u003eDMA: READY\u003c/div\u003e\n            \u003c/div\u003e\n        \u003c/div\u003e\n    \u003c/div\u003e\n\n    \u003cdiv style=\"display: grid; grid-template-columns: 1fr 1fr; gap: 10px;\"\u003e\n        \u003cdiv style=\"background: #fdfdfd; border: 1px solid #eee; border-radius: 4px; position: relative; height: 180px;\"\u003e\n            \u003cdiv style=\"position: absolute; top: 5px; left: 10px; font-size: 10px; font-weight: bold; color: #666;\"\u003eWAVEFORM (1024-SAMPLE DMA)\u003c/div\u003e\n            \u003csvg id=\"adcSvgChart\" viewBox=\"0 0 400 180\" preserveAspectRatio=\"none\" style=\"width:100%; height:100%; display:block;\"\u003e\n                \u003cpath id=\"path-ideal\" d=\"\" fill=\"none\" stroke=\"#ccc\" stroke-width=\"1\" stroke-dasharray=\"3,3\" /\u003e\n                \u003cpath id=\"path-aliased\" d=\"\" fill=\"none\" stroke=\"#007bff\" stroke-width=\"1.5\" /\u003e\n            \u003c/svg\u003e\n        \u003c/div\u003e\n        \u003cdiv style=\"background: #fdfdfd; border: 1px solid #eee; border-radius: 4px; position: relative; height: 180px;\"\u003e\n            \u003cdiv style=\"position: absolute; top: 5px; left: 10px; font-size: 10px; font-weight: bold; color: #666;\"\u003eSPECTRUM (256-BIN WINDOW)\u003c/div\u003e\n            \u003csvg id=\"fftSvgChart\" viewBox=\"0 0 400 180\" preserveAspectRatio=\"none\" style=\"width:100%; height:100%; display:block;\"\u003e\n                \u003cg id=\"fft-bars\"\u003e\u003c/g\u003e\n            \u003c/svg\u003e\n        \u003c/div\u003e\n    \u003c/div\u003e\n    \n    \u003cdiv style=\"margin-top: 15px; display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px;\"\u003e\n        \u003cdiv style=\"padding: 12px; background: #f8f9fa; border-radius: 8px; text-align: center; border: 1px solid #eee;\"\u003e\n            \u003cdiv style=\"font-size: 0.65em; color: #6c757d;\"\u003eTRUE RMS ERROR\u003c/div\u003e\n            \u003cdiv id=\"error-display\" style=\"font-size: 1.1em; font-weight: bold;\"\u003e0.00%\u003c/div\u003e\n        \u003c/div\u003e\n        \u003cdiv style=\"padding: 12px; background: #fffbe6; border-radius: 8px; text-align: center; border: 1px solid #ffe58f;\"\u003e\n            \u003cdiv style=\"font-size: 0.65em; color: #856404;\"\u003eDELTA ERROR (V)\u003c/div\u003e\n            \u003cdiv id=\"delta-display\" style=\"font-size: 1.1em; font-weight: bold; color: #856404;\"\u003e0.000V\u003c/div\u003e\n        \u003c/div\u003e\n        \u003cdiv id=\"compliance-status\" style=\"padding: 12px; border-radius: 8px; text-align: center; display: flex; flex-direction: column; justify-content: center; font-weight: bold; border: 1px solid #eee;\"\u003e\n            \u003cspan id=\"status-text\"\u003eINIT\u003c/span\u003e\n        \u003c/div\u003e\n    \u003c/div\u003e\n\u003c/div\u003e\n\n\u003cscript\u003e\n(function() {\n    const fsSlider = document.getElementById('fs-slider'), thdSlider = document.getElementById('thd-slider');\n    const fsVal = document.getElementById('fs-val'), thdVal = document.getElementById('thd-val');\n    const targetClassSel = document.getElementById('target-class');\n    const errorDisplay = document.getElementById('error-display'), deltaDisplay = document.getElementById('delta-display');\n    const statusText = document.getElementById('status-text'), complianceStatus = document.getElementById('compliance-status');\n    const pathAliased = document.getElementById('path-aliased'), pathIdeal = document.getElementById('path-ideal');\n    const fftBars = document.getElementById('fft-bars');\n\n    const RESOLUTION = 65535, V_REF = 3.3, BUFFER_SIZE = 1024, FFT_WINDOW = 256; \n\n    function getGaussianNoise(sigma) {\n        let u = Math.random(), v = Math.random();\n        return sigma * Math.sqrt(-2.0 * Math.log(u || 0.001)) * Math.cos(2.0 * Math.PI * v);\n    }\n\n    function getAliasedFreq(f, fs) {\n        let n = fs / 2, freq = f % fs;\n        return freq \u003e n ? Math.abs(fs - freq) : freq;\n    }\n\n    \n    function calculateFastDFT(samples) {\n        let n = samples.length;\n        let magnitudes = new Float32Array(n / 2);\n        for (let k = 0; k \u003c n / 2; k++) {\n            let real = 0, imag = 0;\n            for (let t = 0; t \u003c n; t++) {\n                let angle = (6.283185 * k * t) / n; \n                real += samples[t] * Math.cos(angle);\n                imag -= samples[t] * Math.sin(angle);\n            }\n            magnitudes[k] = Math.sqrt(real * real + imag * imag) / (n / 2);\n        }\n        return magnitudes;\n    }\n\n    function update() {\n        let fs = parseInt(fsSlider.value), thd = parseInt(thdSlider.value) / 100;\n        fsVal.innerText = fs; thdVal.innerText = (thd * 100).toFixed(0);\n        \n        let currentBuffer = [], fftWindowBuffer = [];\n        let trueSqSum = 0, dmaSqSum = 0;\n        const harmonics = [{f:50, a:1.1}, {f:150, a:1.1*thd*0.7}, {f:250, a:1.1*thd*0.3}];\n\n        let dAliased = \"\", dIdeal = \"\";\n\n        for (let i = 0; i \u003c BUFFER_SIZE; i++) {\n            let t = i / fs;\n            let sigAlias = 1.65; harmonics.forEach(h =\u003e sigAlias += h.a * Math.sin(6.283185 * getAliasedFreq(h.f, fs) * t));\n            let sigIdeal = 1.65; harmonics.forEach(h =\u003e sigIdeal += h.a * Math.sin(6.283185 * h.f * t));\n\n            let v = sigAlias + getGaussianNoise(0.0008);\n            let inl = (2.5 / RESOLUTION) * V_REF * Math.sin(6.283185 * (v / V_REF));\n            let code = Math.max(0, Math.min(RESOLUTION, Math.round(((v + inl) / V_REF) * RESOLUTION)));\n            let measuredV = (code / RESOLUTION) * V_REF;\n\n            currentBuffer.push({t, ideal: sigIdeal, code});\n            \n            \n            if (i \u003c FFT_WINDOW) {\n                fftWindowBuffer.push(measuredV - 1.65);\n            }\n\n            \n            trueSqSum += Math.pow(sigIdeal - 1.65, 2);\n            dmaSqSum += Math.pow(measuredV - 1.65, 2);\n\n            if (i \u003c 100) { \n                let x = i * 4, yA = 180 - ((code / RESOLUTION) * 180), yI = 180 - ((sigIdeal / V_REF) * 180);\n                dAliased += (i === 0 ? \"M\" : \"L\") + x + \",\" + yA;\n                dIdeal += (i === 0 ? \"M\" : \"L\") + x + \",\" + yI;\n            }\n        }\n\n        pathAliased.setAttribute(\"d\", dAliased);\n        pathIdeal.setAttribute(\"d\", dIdeal);\n\n        \n        const mags = calculateFastDFT(fftWindowBuffer);\n        let barHtml = \"\";\n        mags.forEach((m, idx) =\u003e {\n            if (idx \u003e 0 \u0026\u0026 idx \u003c 64) { \n                let h = Math.min(170, m * 100);\n                barHtml += `\u003crect x=\"${idx * 6}\" y=\"${180 - h}\" width=\"4\" height=\"${h}\" fill=\"#e67e22\" /\u003e`;\n            }\n        });\n        fftBars.innerHTML = barHtml;\n\n        const tRms = Math.sqrt(trueSqSum / BUFFER_SIZE), mRms = Math.sqrt(dmaSqSum / BUFFER_SIZE);\n        const err = Math.abs((mRms - tRms) / tRms);\n        const delta = Math.abs(mRms - tRms);\n\n        errorDisplay.innerText = (err * 100).toFixed(3) + \"%\";\n        deltaDisplay.innerText = delta.toFixed(4) + \"V\";\n\n        const pass = err \u003c= parseFloat(targetClassSel.value);\n        statusText.innerText = pass ? \"PASS\" : \"FAIL\";\n        complianceStatus.style.background = pass ? \"#d4edda\" : \"#f8d7da\";\n        complianceStatus.style.color = pass ? \"#155724\" : \"#721c24\";\n    }\n\n    fsSlider.oninput = update; thdSlider.oninput = update; targetClassSel.onchange = update;\n    update();\n})();\n\u003c/script\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cstrong\u003eLooking for the technical deep-dive?\u003c/strong\u003e Read the full analysis in the \u003ca href=\"/notes/stm32h7-adc-digital-twin/\"\u003eADC Digital Twin Note\u003c/a\u003e.\u003c/p\u003e","title":"ADC Digital Twin \u0026 Metering Lab"},{"content":"Before you spin a PCB or write a line of firmware, you should know whether your hardware choices will meet your accuracy targets. For industrial metering, this is not a nice-to-have—it is the difference between a design that passes certification and one that doesn’t.\nHistorically, modeling these constraints took weeks of mathematical mapping and coding. I wanted to test the waters and see how fast I could build a working, interactive model using modern AI tools.\nA Quick Disclaimer on the Data This project is designed as an indicative Proof of Concept (POC). The goal here is to demonstrate the \u0026ldquo;art of the possible\u0026rdquo; regarding rapid embedded system visualization using AI. Because the focus was on speed, many of the specific technical details, register behaviors, and physical noise parameters were suggested by Gemini as a coding copilot. I have not manually cross-referenced every single parameter against the official STMicroelectronics datasheet to verify what is 100% true or false. Please treat the underlying data as illustrative, structural simulation rather than a production-verified reference.\nWhat We Built This is a browser-based simulation of the STM32H7’s internal 16-bit SAR (Successive Approximation Register) ADC, built specifically for metering and metrology applications.\nThe key word is twin—not just a clean mathematical model. The simulator attempts to replicate the hardware-level constraints that actually matter in a real design: Gaussian thermal noise, INL non-linearity, harmonic aliasing, and the DMA buffer transfer cycle.\nThe architecture mirrors the actual data path inside the MCU:\nSignal Generation Engine: Your simulated grid sensor input. The Silicon Twin: The ADC peripheral with its physical non-idealities injected. Transport Layer: The simulated DMA configuration. Metrology Engine: Your firmware RMS calculation. Change any parameter on the fly, and you see the effect on the final error immediately.\nWhy This Matters Before You Build Anything In IEC 62053 metering, accuracy class defines the maximum allowable RMS error:\nAccuracy Class Max Allowable Error Typical Use Class 0.2 ±0.2% Utility / revenue metering Class 0.5 ±0.5% Industrial metering Class 1.0 ±1.0% Residential / general purpose Hitting these targets depends heavily on how your sampling rate interacts with the harmonics present in the signal. Real-world grid signals are not clean sine waves—they contain 3rd and 5th harmonic distortion from non-linear loads like inverters and motor drives.\nIf your sampling rate drops below twice the frequency of a harmonic, that harmonic aliases—it folds back into the measurement bandwidth at a mirrored frequency. At critical sampling rates, a 250Hz 5th harmonic can alias directly onto the 50Hz fundamental, adding spurious energy to your RMS calculation and pushing you out of your target accuracy class.\nReading the simulator The simulator has two panels and three metric displays.\nWaveform panel (left) shows 100 samples from the full 1024-sample DMA buffer. Two signals are overlaid:\nBlue line — the digitised signal as it would appear in the DMA buffer, including aliasing, thermal noise, and INL distortion Grey dashed line — the ideal analog ground truth At high sampling rates the two lines sit on top of each other. As you drag the sampling rate below the metrological guardrail, watch the blue line begin to warp and develop phase shifts relative to the grey line. That visual separation is harmonic energy folding back into the measurement bandwidth. The divergence happens abruptly at specific critical sampling rates — not gradually.\nSpectrum panel (right) shows the frequency content of the digitised signal using a DFT computed on a 256-sample window. At a default sampling rate of 4kHz you can see all three harmonics as distinct peaks — the 50Hz fundamental, 150Hz 3rd harmonic, and 250Hz 5th harmonic. This is what a clean, well-sampled metrology signal looks like in the frequency domain.\nNote that the FFT panel is most informative at higher sampling rates. At very low rates (below ~400Hz), the harmonic peaks alias to low frequencies and fall outside the visible bin range, so the spectrum appears empty even as the waveform panel clearly shows distortion. Use the waveform panel to observe aliasing behaviour; use the spectrum panel to verify clean harmonic separation at your target operating point.\nThree metric displays sit below the panels:\nTrue RMS Error (%) — percentage difference between the ideal signal RMS and the digitised signal RMS, computed across the full 1024-sample buffer Delta Error (V) — the same difference expressed in absolute volts — this is the raw measurement error your firmware would report to the application layer PASS / FAIL — whether the current error is within the selected IEC 62053 accuracy class Answering Real Design Questions The value of a digital twin—even an indicative one—is the ability to answer architectural questions before touching hardware:\n\u0026ldquo;My grid typically runs at 20% THD. If I sample at 4kHz with a 16-bit ADC, can I theoretically guarantee Class 0.5?\u0026rdquo; \u0026ldquo;At what sampling rate does aliasing actually start visibly distorting my waveform?\u0026rdquo; The fact that we can now conceptualize, draft, and spin up a complex visual simulator in record time using AI completely changes the game for early-stage prototyping.\nWhat the model includes and what it doesn\u0026rsquo;t Included:\nCorrect harmonic aliasing via Nyquist fold-back Gaussian thermal noise (Box-Muller, σ = 0.8mV — realistic for STM32H7 noise floor) INL non-linearity (2.5 LSB amplitude, applied in voltage domain before quantization) 16-bit quantization with correct VREF = 3.3V DMA half-transfer / full-transfer cycle visualisation Metrological oversampling guardrail (triggers below 2500Hz) DFT spectrum display on 256-sample window (harmonic peak visualisation) Delta Error (V) — absolute RMS difference alongside percentage error Not included (future roadmap):\nHigher order harmonics (7th, 9th) — significant in some industrial environments ADC conversion time model — clock-dependent sampling limits DNL (Differential Non-Linearity) — individual code width variation True ping-pong DMA buffer with concurrent CPU processing simulation Try the Live ADC Simulator → ","permalink":"https://manujg.com/notes/stm32h7-adc-digital-twin/","summary":"Before you spin a PCB, you should know whether your hardware choices will meet your accuracy targets. Here is a look at how I used AI to rapidly build a digital twin of the STM32H7 ADC to visualize these constraints.","title":"Rapid Prototyping: A Digital Twin for STM32H7 ADC Metrology"},{"content":"When people talk about AI feeling like a revolution, I get it. The speed, the way you just describe what you want and something working comes out the other end — it really is impressive.\nBut honestly, for me it also feels like something I have seen before. And I think I know why.\n2005, a Turbine Monitoring System, and One Integration Block Early in my career I spent a lot of time with MATLAB, Simulink, and LabVIEW. LabVIEW was the one that stuck — mostly because I figured it out myself on a live project, which I think is why it left such a strong impression.\nFor anyone who hasn\u0026rsquo;t used it, LabVIEW is a graphical programming environment. Instead of writing code you build logic visually, connecting blocks that represent functions and data flows. To a traditional programmer it probably looks like a toy. It is not.\nWhat I liked about it was simple. It got out of the way. Libraries were handled, syntax wasn\u0026rsquo;t a thing, and if something broke you could literally see where the flow had gone wrong. You could focus on the actual problem rather than fighting the tool.\nI was building a predictive monitoring system for industrial turbines at the time. Tracking signals, spotting trends, flagging early indicators of failure. We were working with a university and a PhD student there had written a sophisticated prediction algorithm — impressive work, genuinely.\nMy job was not to rewrite it. It was to integrate it.\nLabVIEW let me drop that external code — whether it was compiled C or a MATLAB script, to be honest I can\u0026rsquo;t remember exactly after all these years — directly into my visual logic as a block. I wired the inputs in, connected the outputs to my dashboard, and it worked.\nIn 2005 that was honestly quite something. Cutting-edge research sitting inside a practical engineering system, and I didn\u0026rsquo;t have to unpick someone else\u0026rsquo;s specialist work to make it happen. I could stay focused on what I was actually there to solve — the signals, the integration, the failure modes. The tool handled the rest.\nTwenty Years Later Over the past year or so I have been running a bunch of personal coding and automation projects — all with AI as a collaborator. Apps Script pipelines for auditing my cloud storage. Home automation scripts that just run quietly in the background. This website, manujg.com, which I built over a single weekend. And quite a few more — there is always something on the go, usually starting from a small problem I got tired of dealing with manually.\nHonestly the pattern is exactly the same as LabVIEW.\nI come in knowing what I want — the architecture, the edge cases, how it should behave. AI handles the first pass. I review it, fix what doesn\u0026rsquo;t work, adjust until it does what I actually need. The gap between having an idea and having something working has shrunk to a point that still catches me off guard sometimes.\nDifferent technology. Same dynamic.\nThe Tool is Only Half of It That turbine system worked not just because LabVIEW was good — it worked because I understood signal processing well enough to know if the outputs actually made sense. I could look at what the algorithm was producing and tell whether it reflected reality. That judgment came from the domain, not from the tool.\nAI is no different. If you can\u0026rsquo;t tell when the output is confidently wrong, you\u0026rsquo;ll just build something broken faster. But if you have that domain knowledge, AI becomes a genuine accelerant. That much I\u0026rsquo;d say without hesitation.\nOne Thing I\u0026rsquo;d Tell Anyone Earlier in Their Career Think in systems before you think in syntax.\nWhat is the problem, really. What does a good solution look like. How does it behave when things go wrong. Does it scale or does it fall apart under load. These are the questions that matter.\nWhat language it gets written in, which library handles a specific function, how the API works — those are implementation details. Important to get right, but not the thing that separates someone who can build from someone who can lead.\nLabVIEW pushed me to think this way early because the visual nature of it forced you to see the system before you got into the detail. AI is doing the same thing now, just in a different way — if you can describe what you want clearly and precisely, the how largely sorts itself out.\nThe tools will keep changing. Thinking clearly about problems won\u0026rsquo;t go out of date. That\u0026rsquo;s the thing worth investing in.\n","permalink":"https://manujg.com/notes/what-labview-taught-me/","summary":"\u003cp\u003eWhen people talk about AI feeling like a revolution, I get it. The speed, the way you just describe what you want and something working comes out the other end — it really is impressive.\u003c/p\u003e\n\u003cp\u003eBut honestly, for me it also feels like something I have seen before. And I think I know why.\u003c/p\u003e\n\u003ch3 id=\"2005-a-turbine-monitoring-system-and-one-integration-block\"\u003e2005, a Turbine Monitoring System, and One Integration Block\u003c/h3\u003e\n\u003cp\u003eEarly in my career I spent a lot of time with MATLAB, Simulink, and LabVIEW. LabVIEW was the one that stuck — mostly because I figured it out myself on a live project, which I think is why it left such a strong impression.\u003c/p\u003e","title":"What LabVIEW Taught Me in 2005 That Made AI Feel Familiar"},{"content":"Push notifications just don\u0026rsquo;t work for important tasks. If you are driving, running errands, or simply focused on something else, it is way too easy to swipe an alert away and completely forget about it. That might be fine for a casual reminder to water the plants, but for things with hard deadlines—like paying a utility bill or making a time-sensitive phone call—missing a notification is a real problem.\nGoogle Tasks is my go-to tool for managing personal to-dos. It is fast and syncs everywhere. But its native notification system relies entirely on those easy-to-miss push alerts. Miss the ping, and you are on your own.\nI needed a reliable system that pushed task reminders directly into my Gmail—a place I actually check consistently. And instead of spending a weekend writing code from scratch, I used Gemini to help me build the whole thing in an afternoon.\nWhat I Built The system works through two distinct workflows:\nInstant Alerts: If a task has a specific time attached to it (e.g., \u0026ldquo;Call the dentist at 2:00 PM\u0026rdquo;), an email lands in my inbox the exact minute that time arrives. The Morning Digest: Every day at 8:00 AM, I get a single HTML summary email covering everything overdue, everything due today, and a heads-up for tomorrow. Color-coded, clean, and scannable in under 30 seconds. Everything runs on Google Apps Script — no external servers, completely free, and entirely in the background.\nThe Architecture \u0026amp; Flow Here is how the system is wired together:\n┌─────────────────────────────────────────────┐ │ Google Tasks API │ └───────────────────┬─────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────┐ │ Google Apps Script Engine │ │ │ │ ┌──────────────────────────────────────┐ │ │ │ ⏱ Every 1 Minute │ │ │ │ Instant Alert │ │ │ │ Scans for time-specific tasks due │ │ │ │ right now → sends immediate email │ │ │ └──────────────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────┐ │ │ │ 🌅 Every Morning at 8 AM │ │ │ │ Daily Digest │ │ │ │ Aggregates overdue + today + │ │ │ │ tomorrow → sends summary email │ │ │ └──────────────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────┐ │ │ │ 🧹 Every Night at 3 AM │ │ │ │ Cleanup Routine │ │ │ │ Removes completed tasks from │ │ │ │ memory → keeps system healthy │ │ │ └──────────────────────────────────────┘ │ │ │ └───────────────────┬─────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────┐ │ Gmail │ │ Instant Alert Email / Morning Digest │ └─────────────────────────────────────────────┘ The Logic Behind Each Part The Instant Alert This trigger runs every 60 seconds and checks whether any task has a specific due time that falls within the last minute.\nThe tricky part was avoiding duplicate emails if the script runs slightly late or the system hiccups. I explained this to Gemini and it suggested giving the script a \u0026ldquo;memory\u0026rdquo; using state management and a concurrency lock. The script now stores the exact timestamp of its last successful run and tags each notified task with a \u0026ldquo;done\u0026rdquo; flag. The lock makes sure two instances can never run simultaneously and flood my inbox.\nThe Daily Digest The morning email is structured for quick triage, not just a raw data dump. Tasks are sorted into three visual buckets: 🔴 Overdue, 🟢 Due Today, and 🔵 Due Tomorrow.\n⏱️ Time-sensitive tasks get a distinct highlight. Any notes attached to a task are preserved in a shaded block below the title, and the task list name is a clickable link that opens Google Tasks directly.\nThe goal was simple: open this email over morning coffee, scan it in 20 seconds, and know exactly what needs attention that day.\nThe Cleanup Routine A common problem with scripts that track state is that their memory grows forever. If the system remembered every task it ever notified me about, it would eventually crash. To fix this, a \u0026ldquo;janitor\u0026rdquo; function runs at 3:00 AM — it finds tasks I\u0026rsquo;ve marked as completed and removes them from the script\u0026rsquo;s memory. The system stays lean and runs indefinitely without any manual intervention.\nWhere Gemini Came In I had a clear picture of what I wanted — the three-trigger structure, the email layout, the triage logic. What I didn\u0026rsquo;t want to do was spend hours debugging API syntax and wrestling with HTML email quirks.\nI used Gemini as a pair-programmer throughout. I\u0026rsquo;d describe a problem in plain English — \u0026ldquo;I need the script to not send the same email twice if it gets delayed\u0026rdquo; — and Gemini would introduce the right concept and write the code to make it work. When the email subject line showed garbled characters on my phone because of an emoji, Gemini immediately diagnosed it and fixed it with a Base64 encoding solution. When I wanted the email to render cleanly on mobile, it handled the single-column HTML layout.\nThe back-and-forth felt genuinely useful. I stayed focused on the logic and experience, Gemini handled the implementation details. The whole thing went from idea to deployed in a few hours.\nTaking It to Work After running this successfully for about six months, I realised the core idea — a consolidated morning brief delivered straight to your inbox — was exactly what I needed at work too.\nI kept the two systems separate since it\u0026rsquo;s a work environment, but took the same architectural pattern and rebuilt it in Microsoft Power Automate. It now aggregates my assigned tasks from MS Planner, Microsoft Loop, and other tools, and sends a unified morning digest to my work email every day. Same concept, different ecosystem — turns out the logic translates whether you\u0026rsquo;re tracking dentist appointments or project deliverables.\nThe Takeaway I now get clean, reliable emails for both my personal tasks and work commitments. I haven\u0026rsquo;t missed a critical task since I set these up.\nMore than the tool itself, this process showed me how useful AI can be as a working partner rather than just a search engine. What could have been a frustrating weekend of Googling Stack Overflow answers turned into a genuinely enjoyable afternoon project. If you\u0026rsquo;re thinking about building something similar or have questions about the setup, drop a comment below.\n","permalink":"https://manujg.com/notes/google-apps-script-automation/","summary":"\u003cp\u003ePush notifications just don\u0026rsquo;t work for important tasks. If you are driving, running errands, or simply focused on something else, it is way too easy to swipe an alert away and completely forget about it. That might be fine for a casual reminder to water the plants, but for things with hard deadlines—like paying a utility bill or making a time-sensitive phone call—missing a notification is a real problem.\u003c/p\u003e\n\u003cp\u003eGoogle Tasks is my go-to tool for managing personal to-dos. It is fast and syncs everywhere. But its native notification system relies entirely on those easy-to-miss push alerts. Miss the ping, and you are on your own.\u003c/p\u003e","title":"I Built My Own Google Tasks Notification System Using AI"},{"content":"This Privacy Policy describes how your personal information is collected, used, and shared when you visit this website.\nAnalytics and Tracking This website uses Google Analytics 4 (GA4) to understand how visitors engage with the site. Google Analytics collects information such as your IP address, browser type, operating system, referring URLs, and pages visited. This data is fully anonymized and is used solely to improve the content and user experience of this site.\nGoogle Analytics uses \u0026ldquo;cookies\u0026rdquo; (data files placed on your device) to track this information. You can read more about how Google uses your Personal Information here: https://policies.google.com/privacy.\nContact Form If you choose to use the contact form on this website, the information you submit (your name, email address, and message) is securely processed by a third-party service, Formspree. This data is used exclusively to route your message to me so that I can reply. You can review how Formspree handles data in their privacy policy here: https://formspree.io/legal/privacy-policy/.\nEmbedded Content Articles on this site may include embedded content (e.g., GitHub Gists, YouTube videos). Embedded content from other websites behaves in the exact same way as if the visitor has visited the other website. These websites may collect data about you, use cookies, embed additional third-party tracking, and monitor your interaction with that embedded content.\nContact If you have questions about this privacy policy, please reach out via the professional contact links provided on the homepage.\n","permalink":"https://manujg.com/privacy/","summary":"\u003cp\u003eThis Privacy Policy describes how your personal information is collected, used, and shared when you visit this website.\u003c/p\u003e\n\u003ch3 id=\"analytics-and-tracking\"\u003eAnalytics and Tracking\u003c/h3\u003e\n\u003cp\u003eThis website uses Google Analytics 4 (GA4) to understand how visitors engage with the site. Google Analytics collects information such as your IP address, browser type, operating system, referring URLs, and pages visited. This data is fully anonymized and is used solely to improve the content and user experience of this site.\u003c/p\u003e","title":"Privacy Policy"},{"content":" Work Twenty years in engineering and R\u0026amp;D — spanning research consultancy, telecom, automotive, and smart metering. I\u0026rsquo;ve worked across teams ranging from a handful of people to 80 or 100, covering everything from embedded systems and integration to full-scale delivery. Most recently, that has involved building scalable firmware platforms, establishing CI/CD pipelines, and driving hardware and firmware test automations alongside digital twin initiatives.\nI\u0026rsquo;ve built teams from scratch and walked into ones that were already on fire. Both taught me things the other couldn\u0026rsquo;t.\nOver time I\u0026rsquo;ve gotten better at spotting where something is quietly going sideways before it becomes a crisis, getting people aligned without it turning into a turf war, and keeping things moving when the situation isn\u0026rsquo;t clean — which it rarely is.\nStill curious about how things work—and how to make them work better. What I\u0026rsquo;m Working On Now Lately I\u0026rsquo;ve been spending a lot of time with AI tools — building small automations, figuring out what\u0026rsquo;s actually useful versus what just looks impressive. The Lab section of this site is where I document that. Most of it started as something I built to solve a problem I actually had.\nIt\u0026rsquo;s also part of thinking through what comes next — both professionally and otherwise. I find that building things, even small ones, is still the best way I know to learn something properly.\nLife\nSomewhere between the work, the experiments and the next idea. I live in Bengaluru with my two kids. Outside of work, I volunteer my time pro bono to facilitate peer support groups and assist with various other community causes. I write here occasionally — about delivery, teams, decisions, and the odd thing I’m still figuring out.\nPatent Applications Two patent applications filed during my time in research — one UK, one international — covering a method for monitoring tyre wear (GB2443965 / WO 2008/059283).\nEducation \u0026amp; Certifications\nExecutive General Management Program, 2018–2019 — Indian Institute of Management (IIM), Bangalore M.Sc. (Telecom), 2003–2004 — Lancaster University, UK B.Tech. (E.C.E.), 1998–2002 — Punjab Technical University Project Management Professional (PMP), 2016 — Project Management Institute Certified Scrum Master (CSM), 2011 — Scrum Alliance Generative AI Leader Specialisation (Google Cloud), 2026 — Coursera The last one is recent and intentionally so. AI is where I\u0026rsquo;m putting time right now, and the certification reflects that — not just a box ticked.\n","permalink":"https://manujg.com/about/","summary":"Engineering Leader. 20+ years leading engineering programs. | Automotive, Telecom, Research","title":"About Me"},{"content":"I have spent 20 years inside engineering programs — running them, scaling them, and delivering them across telecom, automotive, and research environments. That experience is what I bring to advisory work.\nI work with a focused set of engagements at a time. The problems I am most useful for:\nProgram delivery that has stalled. Scope is unclear, ownership is diffuse, the plan exists but nobody trusts it. I help identify what is actually blocking progress and get things moving again.\nEngineering operations at scale. Teams that have grown from 10 to 40 or 50 people and the coordination overhead has started eating the output. Planning, tracking, decision flow — where is the friction, and how do you reduce it without adding more process.\nTechnical leads stepping into management. The shift from individual contributor to delivery owner is harder than it looks. I have navigated that transition across multiple organisations and contexts, and I know where the leverage points are.\nIf any of this is relevant, I am currently open to discussing these challenges.\n","permalink":"https://manujg.com/consulting/","summary":"Helping engineering teams move from plan to delivery.","title":"Advisory"},{"content":"AI is most useful in engineering when it shortens the distance between a question and a testable result. These Notes cover AI-assisted software work, rapid prototypes, simulations and the judgement still required to decide whether a result is trustworthy.\nThe Agentic Workflow I Actually Use for Big Codebases Rapid Prototyping: A Digital Twin for STM32H7 ADC Metrology Explore the Labs Advisory work ","permalink":"https://manujg.com/topics/ai-assisted-engineering/","summary":"How AI changes the way complex engineering work can be explored and shipped.","title":"AI-Assisted Engineering"},{"content":"Let’s talk directly If you want to discuss an engineering project, an advisory role, AI-assisted engineering or an early product idea, email me directly.\nEmail Manuj directly\nmanujg@gmail.com\nI usually respond within a couple of days.\nThere is no contact form here. You can write in your own email app, see exactly where the message is going, and keep a copy of the conversation.\n","permalink":"https://manujg.com/contact/","summary":"Get in touch directly.","title":"Contact"},{"content":"Embedded systems leadership sits between technical depth, delivery discipline and the ability to make trade-offs visible. This topic brings together Notes on engineering organisations, resilient systems, embedded software and practical execution.\nTriple-Hop Network Audit: Catching Micro-Outages with a Custom Monitor Rapid Prototyping: A Digital Twin for STM32H7 ADC Metrology Explore the Labs Advisory work ","permalink":"https://manujg.com/topics/embedded-systems-leadership/","summary":"Engineering leadership, delivery governance and practical systems thinking.","title":"Embedded Systems Leadership"},{"content":"I don’t believe in over-complicating management. Over the years, I’ve found that a few simple, consistent habits are what actually make the difference between a project that ships and one that gets stuck.\nThese are the three main frameworks I use:\n1. Regular Cadence A program needs a heartbeat. I set up a regular rhythm of short, focused meetings designed specifically to find and fix bottlenecks fast. It’s not about status reporting; it’s about clearing the path for the team so they can keep moving.\n2. The DRI Framework Borrowed from Apple, the Directly Responsible Individual (DRI) concept ensures that every task, risk, or decision has exactly one name next to it. When \u0026ldquo;everyone\u0026rdquo; is responsible, no one is. This brings total clarity to who is owning the outcome.\n3. The Innovation Box Engineering teams need space to try new things, but that shouldn\u0026rsquo;t derail the main delivery schedule. I use the Innovation Box to create a \u0026ldquo;safe zone\u0026rdquo; where we can experiment and fail fast without putting the primary project at risk.\nI am currently writing detailed guides for each of these. They will be linked here soon.\n","permalink":"https://manujg.com/frameworks/","summary":"Simple tools I use to keep programs moving.","title":"Frameworks"},{"content":"Thanks for the message.\nI’ve received it and will get back to you as soon as I can. In the meantime, feel free to head back to the homepage or check out Labs.\n","permalink":"https://manujg.com/success/","summary":"Thank you for reaching out.","title":"Message Sent"}]